【发布时间】:2015-12-01 23:24:35
【问题描述】:
我无法正确解析 CSV 文件。数据行中的某些值可以为空白,当我在任何值行中有空白条目时,my code 无法正常工作。如果没有空白条目,程序将返回以下结果:
Symbol: GOOG
Name: Googl Inc.
Price: $570.25
High Today: $570.25
Low Today: $560.35
Symbol: APPL
Name: Apple Inc.
Price: $123.25
High Today: $124.25
Low Today: $125.35
如果我使用以下 CSV 字符串运行相同的程序,程序会因断言错误而停止。这是由于解析器跳过了 adjacent ,, delimiters,因此数据行中的列数与标题中的列数不匹配。
std::stringstream ifs(
"Symbol,Name,Price,High Today,Low Today\n"
"GOOG,Googl Inc.,$570.25 ,$570.25 ,$560.35\n"
"APPL,Apple Inc.,$123.25 ,,$125.35\n");
这是我的代码:
#include <iostream>
#include <vector>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <cassert>
#include <locale>
// This ctype facet classifies commas and endlines as whitespace
struct csv_whitespace : std::ctype<char> {
static const mask* make_table() {
// make a copy of the "C" locale table
static std::vector<mask> v(classic_table(), classic_table() + table_size);
v[','] |= space; // comma will be classified as whitespace
v[' '] &= ~space; // space will not be classified as whitespace
return &v[0];
}
csv_whitespace(std::size_t refs = 0)
: ctype(make_table(), false, refs)
{}
};
static int row_end = std::ios_base::xalloc();
std::istream& record(std::istream& is) {
while (std::isspace(is.peek(), is.getloc())) {
int c(is.peek());
is.ignore();
if (c == '\n') {
is.iword(row_end) = 1;
is.setstate(std::ios_base::failbit);
}
}
return is;
}
template<class Iter1, class Iter2, class Function>
void for_each_binary_range(Iter1 first1, Iter1 last1,
Iter2 first2, Iter2 last2, Function f)
{
assert(std::distance(first1, last1) <=
std::distance(first2, last2));
while (first1 != last1) {
f(*first1++, *first2++);
}
}
int main(int argc, char *argv[])
{
std::stringstream ifs(
"Symbol,Name,Price,High Today,Low Today\n"
"GOOG,Googl Inc.,$570.25 ,$570.25 ,$560.35\n"
"APPL,Apple Inc.,$123.25 ,$124.25 ,$125.35\n");
//std::ifstream ifs("c:\\temp\\csvfile.csv", std::ios::in);
std::vector<std::string> keys, values;
ifs.imbue(std::locale(ifs.getloc(), new csv_whitespace));
bool bHeaderProcessed = false;
for (std::string item;;) {
if (ifs >> record >> item) {
if (!bHeaderProcessed) {
keys.push_back(item);
} else {
values.push_back(item);
}
} else if (ifs.eof()) {
// catch case where last line does not have trailing \n
if (!values.empty()) {
for_each_binary_range(std::begin(keys), std::end(keys),
std::begin(values), std::end(values),
[&](std::string const& key, std::string const& value) {
std::cout << key << ": " << value << std::endl;
std::cout << std::endl;
});
values.clear();
}
break;
} else if (ifs.iword(row_end)) {
// reset eol flag & clear stream state
ifs.iword(row_end) = 0;
// clear the fail-bit so we can stream more values
ifs.clear();
bHeaderProcessed = true;
if (!values.empty()) {
for_each_binary_range(std::begin(keys), std::end(keys),
std::begin(values), std::end(values),
[&](std::string const& key, std::string const& value) {
std::cout << key << ": " << value << std::endl;
});
values.clear();
std::cout << std::endl;
}
} else {
break;
}
}
return -1;
}
我基于我的原始代码记录良好here。不幸的是,问题的答案(带有现场演示here)似乎无法处理多行的情况,我无法处理令牌为空的情况。
我的版本将每一行打印为一系列名称/值,它还处理多行或一行未以新行结尾的情况。
上面的链接答案中很好地描述了逻辑
有人可以指出如何处理我在 csv 的数据行中有相邻分隔符的情况。
【问题讨论】:
-
我们需要查看您的代码来帮助您。经过一整天的回答问题,我们的精神力量现在有点弱。 ;)
-
@johnco3 @ user4581301 不,这就是你所说的读取失败。我不习惯加粗的蓝色链接,我想我掩盖了它们。对于我的这个错误,我深表歉意。
-
别担心,@soulsabr。稍后我将把我评论的重要内容移植到新评论并删除旧评论,这样我们就可以从头开始。
-
伙计们,我通过将代码嵌入到 clafity 中来解决了这个问题 - 道歉和好点,感谢建设性的 cmets
-
这一点已经变得毫无意义。谢谢你,@johnco3