【问题标题】:Splitting a line of a csv file into a std::vector?将一行 csv 文件拆分为 std::vector?
【发布时间】:2012-07-03 12:31:04
【问题描述】:

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

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

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

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

text1,tex2,

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

【问题讨论】:

  • CSV parser in C++ 的可能重复项
  • 不,不是。如果该行以逗号结尾,则该代码的作用完全相同
  • 不是分隔符的问题吗? std::getline 提取直到找到分隔符。但是对于最后一项,没有下一个分隔符,,因此没有提取任何内容,因此 while 循环结束。
  • @Jonnster:仅仅因为当前接受的答案有缺陷并不意味着另一个问题没有充分解决相同的问题空间 - 还有其他答案应该有效并且可以被投票,你可以用具体答案评论问题。

标签: c++ csv vector std


【解决方案1】:

您可以使用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"
""

【讨论】:

  • 为什么? using namespace std 是很正常的事情,不是吗? @Seanny123 我认为删除它不会有任何效果,但请随意在您自己的代码中这样做。
【解决方案2】:
bool addEmptyLine = sText.back() == ',';

/* your code here */

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

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

/* your code */

assert(m_vecFields.size() == 3);

【讨论】:

  • 是的,我只是像第一个示例一样添加了代码,然后回来发现有人提出了建议。这不是最干净的代码,但可以。
【解决方案3】:

你可以使用类似这样的函数:

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

【讨论】:

    【解决方案4】:

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

    来源 - 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;
    }
    

    【讨论】:

      【解决方案5】:

      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());
      

      顺便说一句,如果您只是想从 CSV string(例如 item)构造 vector&lt;string&gt;,您可以这样做:

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

      [Live Example]

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

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

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

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

      【讨论】:

        猜你喜欢
        • 2016-08-17
        • 2016-07-26
        • 2014-06-09
        • 2021-07-12
        • 1970-01-01
        • 1970-01-01
        • 2012-12-03
        • 2019-11-07
        相关资源
        最近更新 更多