2013-07-19 160 views
2

我正在构建C++ CSV数据解析器。我试图访问文件的第一列和第十五列,并使用getline命令将它们读入两个数组中。例如:C++使用逗号分隔CSV引号

for(int j=0;j<i;j++) 
{ 
    getline(posts2,postIDs[j],','); 
    for(int k=0;k<14;k++) 
    { 
     getline(posts2,tossout,','); 
    } 
    getline(posts2,answerIDs[j],','); 
    getline(posts2,tossout,'\r'); 

但是,在两者之间的第一和第十五列是一列,它是在引号和包含各种逗号和松散引号。例如: “ABC,defghijk ”

...,Lmnopqrs, “TUV”,“ WXYZ”,... <

什么将最好的方式,以避免此列中?由于里面有引号和逗号,我无法对它进行细化。运行后,我应该阅读引用的垃圾字符,直到我发现“,依次?

此外,我已经看到其他解决方案,但它们都是Windows/Visual Studio中,我运行的Mac OSX版本。10.8.3和Xcode 3.2.3。

提前感谢! 德鲁

+1

相关(接近重复):http://stackoverflow.com/a/1603175/179910 –

回答

6

没有为CSV格式没有正式的标准,但我们注意到,在一开始就 您引用的丑列:

"abc, defghijk. "Lmnopqrs, "tuv,"" wxyz.", 

不符合什么被认为是CSV的Basic Rules, 因为其中的两个是: -

  • 1)中的字段嵌入的逗号必须加引号。

  • 2)每个嵌入的双引号字符都必须用一对双引号字符表示。

如果问题列服从规则1),那么它不遵守规则2)。但我们可以解释它,以遵守规则1) - 所以我们可以说它在哪里结束 - 如果我们平衡双引号,例如

[abc, defghijk. [Lmnopqrs, ]tuv,[] wxyz.], 

平衡的最外层引号包围列。平衡内部报价 可以只是缺乏任何其他内部指示,除了平衡 使它们内部。

我们希望能有规则,它将分析这个文本作为一列, 始终与规则1),并且还将解析 遵守规则2)也列。刚刚展示的平衡表明此 可以完成,因为遵守两个规则的列必须是可平衡的。

建议的规则是:

  • A柱延伸到由0双引号之前或 由最后的偶数双引号的后面的第一个逗号。

如果有任何偶数双引号到逗号,那么我们就知道 我们可以平衡封闭的报价,并且在至少一种方式平衡休息。

你正在考虑的比较简单的规则:

运行到报价后,我应该读引述垃圾字符一个字符,直到我发现”,依次

会?如果它与某些列是服从规则2),如

“超级‘’豪华”“卡车”遇到失败,

更简单的规则将在""luxurious""后终止列。但由于 此栏符合规则2),相邻的双引号是“转义”双引号,没有定界的意义。另一方面,建议的 规则仍然正确解析列,在truck"后终止它。

这里是一个演示程序,其中功能get_csv_column通过建议的规则解析列 :

#include <iostream> 
#include <fstream> 
#include <cstdlib> 

using namespace std; 

/* 
    Assume `in` is positioned at start of column. 
    Accumulates chars from `in` as long as `in` is good 
    until either:- 
     - Have consumed a comma preceded by 0 quotes,or 
     - Have consumed a comma immediately preceded by 
     the last of an even number of quotes. 
*/ 
std::string get_csv_column(ifstream & in) 
{ 
    std::string col; 
    unsigned quotes = 0; 
    char prev = 0; 
    bool finis = false; 
    for (int ch; !finis && (ch = in.get()) != EOF;) { 
     switch(ch) { 
     case '"': 
      ++quotes; 
      break; 
     case ',': 
      if (quotes == 0 || (prev == '"' && (quotes & 1) == 0)) { 
       finis = true; 
      } 
      break; 
     default:; 
     } 
     col += prev = ch; 
    } 
    return col; 
} 

int main() 
{ 
    ifstream in("csv.txt"); 
    if (!in) { 
     cout << "Open error :(" << endl; 
     exit(EXIT_FAILURE); 
    } 
    for (std::string col; in;) { 
     col = get_csv_column(in), 
     cout << "<[" << col << "]>" << std::endl; 
    } 
    if (!in && !in.eof()) { 
     cout << "Read error :(" << endl; 
     exit(EXIT_FAILURE); 
    } 
    exit(EXIT_SUCCESS); 
} 

它包围每一列中<[...]>,不贴现换行符和 包括终端“”与每个列:

文件csv.txt是:

...,"abc, defghijk. "Lmnopqrs, "tuv,"" wxyz.",..., 
",","", 
Year,Make,Model,Description,Price, 
1997,Ford,E350,"Super, ""luxurious"", truck", 
1997,Ford,E350,"Super, ""luxurious"" truck", 
1997,Ford,E350,"ac, abs, moon",3000.00, 
1999,Chevy,"Venture ""Extended Edition""","",4900.00, 
1999,Chevy,"Venture ""Extended Edition, Very Large""",,5000.00, 
1996,Jeep,Grand Cherokee,"MUST SELL! 
air, moon roof, loaded",4799.00, 

输出是:

<[...,]> 
<["abc, defghijk. "Lmnopqrs, "tuv,"" wxyz.",]> 
<[...,]> 
<[ 
",",]> 
<["",]> 
<[ 
Year,]> 
<[Make,]> 
<[Model,]> 
<[Description,]> 
<[Price,]> 
<[ 
1997,]> 
<[Ford,]> 
<[E350,]> 
<["Super, ""luxurious"", truck",]> 
<[ 
1997,]> 
<[Ford,]> 
<[E350,]> 
<["Super, ""luxurious"" truck",]> 
<[ 
1997,]> 
<[Ford,]> 
<[E350,]> 
<["ac, abs, moon",]> 
<[3000.00,]> 
<[ 
1999,]> 
<[Chevy,]> 
<["Venture ""Extended Edition""",]> 
<["",]> 
<[4900.00,]> 
<[ 
1999,]> 
<[Chevy,]> 
<["Venture ""Extended Edition, Very Large""",]> 
<[,]> 
<[5000.00,]> 
<[ 
1996,]> 
<[Jeep,]> 
<[Grand Cherokee,]> 
<["MUST SELL! 
air, moon roof, loaded",]> 
<[4799.00]> 
+0

+1我认为这个问题的答案应该得到至少一票! – Watusimoto

+0

我正在使用函数get_csv_column()来解析超过32KB的CSV数据。串流正在变得垃圾。 – NJMR