4

我有一个函数可以逐行读取 CSV 文件。对于每一行,它将该行拆分为一个向量。执行此操作的代码是

    std::stringstream ss(sText);
    std::string item;

    while(std::getline(ss, item, ','))
    {
        m_vecFields.push_back(item);
    }

这工作正常,除非它读取最后一个值为空白的行。例如,

text1,tex2,

我希望它返回一个大小为 3 的向量,其中第三个值为空。但是,它只返回一个大小为 2 的向量。我该如何纠正这个问题?

4

5 回答 5

4

你可以用boost::split它来为你做这一切。
http://www.boost.org/doc/libs/1_50_0/doc/html/string_algo/usage.html#id3207193

它具有您在一行中需要的行为。

示例 boost::split 代码

#include <iostream>
#include <vector>
#include <boost/algorithm/string.hpp>

using namespace std;

int main()
{
    vector<string> strs;

    boost::split(strs, "please split,this,csv,,line,", boost::is_any_of(","));

    for ( vector<string>::iterator it = strs.begin(); it < strs.end(); it++ )
        cout << "\"" << *it << "\"" << endl;

    return 0;
}

结果

"please split"
"this"
"csv"
""
"line"
""
于 2012-07-03T13:52:32.717 回答
2
bool addEmptyLine = sText.back() == ',';

/* your code here */

if (addEmptyLine) m_vecFields.push_back("");

或者

sText += ',';     // text1, text2,,

/* your code */

assert(m_vecFields.size() == 3);
于 2012-07-03T13:00:11.843 回答
2

您可以使用类似于此的功能:

template <class InIt, class OutIt>
void Split(InIt begin, InIt end, OutIt splits)
{
    InIt current = begin;
    while (begin != end)
    {
        if (*begin == ',')
        {
            *splits++ = std::string(current,begin);
            current = ++begin;
        }
        else
            ++begin;
    }
    *splits++ = std::string(current,begin);
}

它将遍历字符串,每当遇到分隔符时,它将提取字符串并将其存储在拆分迭代器中。
有趣的部分是

  • 当 current == 开始时,它将插入一个空字符串(测试用例:“text1,,tex2”)
  • 最后一次插入保证总是有正确数量的元素。
    如果结尾有逗号,则会触发上一个项目符号点并添加一个空字符串,否则会将最后一个元素添加到向量中。

你可以像这样使用它:

std::stringstream ss(sText);
std::string item;
std::vector<std::string> m_vecFields;
while(std::getline(ss, item))
{
    Split(item.begin(), item.end(), std::back_inserter(m_vecFields));
}

std::for_each(m_vecFields.begin(), m_vecFields.end(), [](std::string& value)
{
    std::cout << value << std::endl;
});
于 2012-07-03T13:32:21.057 回答
2

C++11 使得使用regex_token_iterator处理转义逗号变得非常容易:

std::stringstream ss(sText);
std::string item;
const regex re{"((?:[^\\\\,]|\\\\.)*?)(?:,|$)"};

std::getline(ss, item)

m_vecFields.insert(m_vecFields.end(), sregex_token_iterator(item.begin(), item.end(), re, 1), sregex_token_iterator());

顺便说一句,如果您只是想vector<string>从 CSV构造一个,string例如item您可以这样做:

const regex re{"((?:[^\\\\,]|\\\\.)*?)(?:,|$)"};
vector<string> m_vecFields{sregex_token_iterator(item.begin(), item.end(), re, 1), sregex_token_iterator()};

[现场示例]

一些快速的解释regex可能是有序的。(?:[^\\\\,]|\\\\.)匹配转义字符或非','字符。(有关更多信息,请参见此处:https ://stackoverflow.com/a/7902016/2642059 )这*?意味着它不是贪婪匹配,因此它将在第一次 ','到达时停止。所有嵌套在一个捕获中,由最后一个参数 the 1, to 选择regex_token_iterator。最后,(?:,|$)将匹配','-delimiter 或string.

为了使这个标准的 CSV 阅读器忽略空元素,可以更改正则表达式以仅匹配具有多个字符的字符串。

const regex re{"((?:[^\\\\,]|\\\\.)+?)(?:,|$)"};

请注意,'+'现在已经替换了'*'指示的 1 个或多个匹配字符。这将阻止它匹配以 .item结尾的字符串','。你可以在这里看到一个例子:http: //ideone.com/W4n44W

于 2015-03-05T14:45:55.680 回答
2

解析 csv 文件的灵活解决方案:其中:

source - CSV 文件的内容

分隔符 - CSV 分隔符,例如。',' ';'

std::vector<std::string> csv_split(std::string source, char delimeter) {
    std::vector<std::string> ret;
    std::string word = "";
    int start = 0;

    bool inQuote = false;
    for(int i=0; i<source.size(); ++i){
        if(inQuote == false && source[i] == '"'){
            inQuote = true;
            continue;
        }
        if(inQuote == true && source[i] == '"'){
            if(source.size() > i && source[i+1] == '"'){
                ++i;
            } else {
                inQuote = false;
                continue;
            }
        }

        if(inQuote == false && source[i] == delimeter){
            ret.push_back(word);
            word = "";
        } else {
            word += source[i];
        }
    }
    ret.push_back(word);

    return ret;
}
于 2015-03-22T16:17:39.910 回答