【问题标题】:How does one correctly store data into an array struct with stringstream? [duplicate]如何使用 stringstream 将数据正确存储到数组结构中? [复制]
【发布时间】:2017-01-02 13:38:42
【问题描述】:

我想知道如何将 CSV 文件中的数据存储到结构化数组中。我意识到我需要使用 getline 等,到目前为止我已经想出了这个代码:

这是我的结构:

struct csvData //creating a structure
{
     string username; //creating a vector of strings called username
     float gpa; //creating a vector of floats called gpa
     int age; //creating a vector of ints called age
};

这是我的数据阅读器和存储数据的部分:

csvData arrayData[10];
string data;
ifstream infile; //creating object with ifstream
infile.open("datafile.csv"); //opening file
if (infile.is_open()) //error check

int i=0;
while(getline(infile, data));
{
    stringstream ss(data);
    ss >> arrayData[i].username;
    ss >> arrayData[i].gpa;
    ss >> arrayData[i].age;
    i++;
}

此外,这就是我尝试打印信息的方式:

for (int z = 0; z<10; z++)
    {
        cout<<arrayData[z].username<<arrayData[z].gpa<<arrayData[z].age<<endl;
    }

但是,当运行这个命令时,我得到了一个似乎是随机数的 cout:

1.83751e-0383 03 4.2039e-0453 1.8368e-0383 07011688

我认为这必须是运行的数组未正确存储变量,因此我正在读取随机内存插槽,但是我不确定。

最后,这是我正在尝试读取的 CSV 文件。

username,gpa,age
Steven,3.2,20
Will,3.4,19
Ryan,3.6,19
Tom,3,19

【问题讨论】:

    标签: c++ csv c++11 io stringstream


    【解决方案1】:

    您的解析代码中没有任何内容实际上会尝试将单行解析为各个字段:

    while(getline(infile, data));
    {
    

    这会正确地将输入文件中的一行读入data 字符串。

     stringstream ss(data);
    
     ss >> arrayData[i].username;
     ss >> arrayData[i].gpa;
     ss >> arrayData[i].age;
    

    您需要尝试to explain to your rubber duck 这应该如何采用单行逗号分隔值,就像您在问题中显示的那样:

     Steven,3.2,20
    

    并用逗号将该字符串分隔为单独的值。 &gt;&gt; 运算符不会执行此操作。 operator&gt;&gt; 使用空格而不是逗号分隔输入。您的怀疑是正确的,您没有正确解析输入。

    这是您必须自己完成的任务。我假设您希望作为学习经验或作为家庭作业,自己手动完成此操作。那么,你自己做吧。 data 中有一行。使用 C++ 提供的任意数量的工具:std::stringfind() 方法,或来自&lt;algorithm&gt;std::find(),查找data 字符串中的每个逗号,然后提取字符串的每个单独部分每个逗号之间的字符串。然后,您仍然需要将两个数字字段转换为适当的数据类型。这就是你将它们中的每一个放入std::istringstream,并使用operator&gt;&gt; 将它们转换为数字类型的时候。

    但是,说了这么多,还有另一种肮脏的伎俩,可以快速解决这个问题。回想一下data 中的原始行包含

     Steven,3.2,20
    

    你所要做的就是用空格替换逗号,把它变成:

     Steven 3.2 20
    

    std::replace() 或小循环替换逗号很简单。然后,您可以将结果填充到 std::istringstream 中,并使用您已经编写的代码使用 operator&gt;&gt; 将各个空格分隔的值提取到离散变量中。

    只是一个小小的警告:如果这确实是你的家庭作业,编写代码来手动解析和提取逗号分隔的值,不能保证你的老师会给你完整的分数让你采取肮脏的伎俩...

    【讨论】:

    • 我一直在尝试,但是我一直在尝试解析第一个字符串。为什么这不起作用:while(getline(infile,data, ','))
    • 因为不是文件中的每个值都后跟逗号字符。每三个值后跟一个换行符。这就是文件中有多行的原因。你看,一个程序完全按照你告诉它做的事情做,而不是你想让它做的事情。您告诉它读取到下一个逗号字符。所以这就是它要做的事情。如果它看到一个换行符,你的程序不会太在意,它被告知读取直到它看到一个逗号,所以它会继续读取直到它看到下一行的逗号,将所有内容与上一行的最后一个值一起挤压, 作为一个字符串。
    • 嘿伙计,我在解析数据文件时遇到了很多麻烦。有什么办法可以给我一些示例代码的提示吗?我已经卡了 5 天了...
    • 我的回答已经给出了两种不同的方法来解析文件。其中之一只需要在您已经编写并包含在问题中的代码中添加一行代码。你需要重新阅读我的答案。
    【解决方案2】:

    正在建设中

    吨,很好的尝试和很好的完整问题。答案如下:

    1) 循环后有一个分号:

    while(getline(infile, data));
    

    删除它。

    我是如何轻松搞定的?我编译时启用了所有警告,如下所示:

    C02QT2UBFVH6-lm:~ gsamaras$ g++ -Wall main.cpp
    main.cpp:24:33: warning: while loop has empty body [-Wempty-body]
        while(getline(infile, data));
                                    ^
    main.cpp:24:33: note: put the semicolon on a separate line to silence this warning
    1 warning generated.
    

    事实上,你也应该在没有-Wall 的情况下收到该警告,但开始使用它,它对你也有好处! :)

    2) 然后,您读取了一些元素,但没有读取 10,那么为什么要打印 10?尽可能多地打印您实际阅读的内容,即i

    当您尝试打印数组的所有 10 个元素时,您会打印未初始化的元素,因为您没有初始化结构数组。

    此外,datafile.csv 中的行数少于 10。因此,您开始填充数组,但当文件没有更多行时,您停止了。因此,您的数组中的一些元素(最后 6 个元素)仍未初始化。

    打印未初始化的数据,导致未定义的行为,这就是您看到垃圾值的原因。

    3) 还有这个:

    if (infile.is_open()) //error check
    

    可以这样写:

    if (!infile.is_open())
      cerr << "Error Message by Mr. Tom\n";
    

    把它们放在一起:

    仍然无法正常工作,因为ss &gt;&gt; arrayData[i].username; 吃掉了整个输入行,接下来的两次提取失败,正如 Pete Becker 所说,但我把它留在这里,这样其他人就不会做出同样的尝试!!!!!! !!

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <sstream>
    
    using namespace std;
    
    struct csvData //creating a structure
    {
         string username; //creating a vector of strings called username
         float gpa; //creating a vector of floats called gpa
         int age; //creating a vector of ints called age
    };
    
    int main()
    {
        csvData arrayData[10];
        string data;
        ifstream infile; //creating object with ifstream
        infile.open("datafile.csv"); //opening file
        if (!infile.is_open()) { cerr << "File is not opened..\n"; }
    
        int i=0;
        while(getline(infile, data))
        {
            stringstream ss(data);
            ss >> arrayData[i].username;
            ss >> arrayData[i].gpa;
            ss >> arrayData[i].age;
            i++;
        }
    
        for (int z = 0; z< i; z++)
        {
            cout<<arrayData[z].username<<arrayData[z].gpa<<arrayData[z].age<<endl;
        }
    
        return 0;
    }
    

    输出:

    C02QT2UBFVH6-lm:~ gsamaras$ g++ -Wall main.cpp
    C02QT2UBFVH6-lm:~ gsamaras$ ./a.out 
    username,gpa,age00
    Steven,3.2,2000
    Will,3.4,1900
    Ryan,3.6,1900
    Tom,3,1900
    

    但是等一下,现在它可以工作了,但是为什么会这样:

    while(getline(infile, data));
    {
       ...
    }
    

    没有?

    因为在循环后面加分号就相当于:

    while()
    { 
      ;
    }
    

    因为您可能已经知道只有一行作为主体的循环不需要大括号。

    我认为是循环体的部分(即你使用的部分std::stringstream)发生了什么?

    它被执行了! 但只有一次!

    你看,一对花括号本身就意味着什么,它是一个匿名作用域/块。

    所以这个:

    {
        stringstream ss(data);
        ss >> arrayData[i].username;
        ss >> arrayData[i].gpa;
        ss >> arrayData[i].age;
        i++;
    }
    

    按照您的意图运行,而不是 while 循环的一部分!

    为什么它起作用了?!因为你在循环之前已经声明了i! ;)

    【讨论】:

    • 这并没有解决核心问题,即ss &gt;&gt; arrayData[i].username; 吃掉了整个输入行,接下来的两次提取失败。
    • @PeteBecker 该死的,远远错过了!谢谢,打印没有帮助。有什么想法吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-22
    • 2015-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-27
    相关资源
    最近更新 更多