我正在构建一个C ++ CSV数据解析器。我正在尝试访问文件的第一列和第十五列,并使用getline
命令将它们读入两个数组。例如:
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。”,...&lt;
避免使用此列的最佳方法是什么?我无法对它进行深入研究,因为它内部有引号和逗号。在报到之后,我应该逐个阅读引用的垃圾字符,直到我按顺序找到“,?
另外,我见过其他解决方案,但所有解决方案都是Windows / Visual Studio独有的。我正在运行Mac OSX ver。 10.8.3 with Xcode 3.2.3。
提前致谢! 德鲁
答案 0 :(得分:9)
CSV格式没有正式标准,但我们从一开始就注意到 你引用的那个丑陋的专栏:
"abc, defghijk. "Lmnopqrs, "tuv,"" wxyz.",
不符合被视为CSV的Basic Rules, 因为其中两个是: -
1)必须引用包含逗号的字段。
2)每个嵌入的双引号字符必须用一对双引号字符表示。
如果问题列遵守规则1),则它不遵守规则2)。但我们可以解释它 遵守规则1) - 所以我们可以说它结束的地方 - 如果我们平衡双引号,例如。
[abc, defghijk. [Lmnopqrs, ]tuv,[] wxyz.],
平衡的最外层引号将列括起来。平衡的内部报价 可能只是缺乏任何其他内部的迹象,除了平衡 使它们成为内部的。
我们想要一个将此文本解析为一列的规则, 与规则1)一致,并且还将解析列 做也遵守规则2)。刚展出的平衡表明了这一点 可以完成,因为遵守这两个规则的列必然是 也是平衡的。
建议的规则是:
如果逗号之前有双引号,那么我们就知道了 我们可以平衡封闭的报价,并至少以一种方式平衡其余的报价。
您正在考虑的更简单的规则:
在报到之后,我应该逐字逐句地阅读引用的垃圾,直到找到“,依次?”
如果符合做遵守规则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]>