如何使用stringstream将数据正确存储到数组结构中?

时间:2016-08-26 00:52:50

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

我想知道如何将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

2 个答案:

答案 0 :(得分:3)

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

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

并用逗号将该字符串分隔成单个值。这样做的>>运算符没有任何意义。 operator>>使用空格而不是逗号分隔输入。您的怀疑是正确的,您没有正确解析输入。

这是你必须自己完成的任务。我假设您希望,作为一种学习经历,或作为家庭作业,自己手动完成这项工作。好吧,那么,自己动手吧。您在data中有一行。使用C ++为您提供的任意数量的工具:std::string的{​​{1}}方法或find()的{​​{1}}(),以查找std::find中的每个逗号{1}}字符串,然后提取每个逗号之间的字符串的每个单独部分。然后,您仍然需要将两个数字字段转换为适当的数据类型。当你将每一个放入<algorithm>时,并使用data将它们转换为数字类型。

但是,尽管如此,还有另一种肮脏的技巧,可以快速解决这个问题。回想一下std::istringstream中的原始行包含

operator>>

您所要做的就是用空格替换逗号,将其变为:

data

使用 Steven,3.2,20 或使用小循环替换带空格的逗号是微不足道的。然后,您可以将结果填充到 Steven 3.2 20 中,并使用std::replace()使用您已编写的代码将单个空格分隔的值提取到离散变量中。

只是一个小小的警告:如果这确实是你的家庭作业,要编写代码来手动解析和提取逗号分隔的值,就不能保证你的导师会给你完整的成绩。脏兮兮的做法......

答案 1 :(得分:2)

正在施工

Ton,很好的尝试和完整的问题。这是答案:

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 >> 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));
{
   ...
}

没&#39;?吨

因为,在循环之后加一个分号就等于:

while()
{ 
  ;
}

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

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

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

你看,一对大括号本身就意味着什么,它是一个匿名的范围/块。

所以这个:

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

在它的一个上运行,而不是像你想要的那样成为while循环的一部分!

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