解析以逗号分隔的std :: string

Pik*_*iku 122 c++ csv string parsing stl

如果我有一个包含以逗号分隔的数字列表的std :: string,那么解析数字并将它们放在整数数组中的最简单方法是什么?

我不想将其概括为解析其他任何内容.只是一个逗号分隔整数的简单字符串,如"1,1,1,1,2,1,1,1,0".

use*_*321 142

#include <vector>
#include <string>
#include <sstream>
#include <iostream>

int main()
{
    std::string str = "1,2,3,4,5,6";
    std::vector<int> vect;

    std::stringstream ss(str);

    for (int i; ss >> i;) {
        vect.push_back(i);    
        if (ss.peek() == ',')
            ss.ignore();
    }

    for (std::size_t i = 0; i < vect.size(); i++)
        std::cout << vect[i] << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,它会,但空格不是最初问题的一部分 (32认同)
  • 我认为如果之前有空格,这将失败, (9认同)
  • @safe_malloc:Shoudnt这是`while(ss.peek()==','|| ss.peek()=='')` (5认同)
  • 为了掩盖:`if(ss.peek()==','|| ss.peek()=='')` (4认同)

Zoo*_*tor 106

一些不那么冗长,std并用逗号分隔的东西.

stringstream ss( "1,1,1,1, or something else ,1,1,1,0" );
vector<string> result;

while( ss.good() )
{
    string substr;
    getline( ss, substr, ',' );
    result.push_back( substr );
}
Run Code Online (Sandbox Code Playgroud)

  • 易于阅读,并且可以很好地处理空白。谢谢! (4认同)

Jer*_*fin 61

另一种相当不同的方法:使用将逗号视为空格的特殊区域设置:

#include <locale>
#include <vector>

struct csv_reader: std::ctype<char> {
    csv_reader(): std::ctype<char>(get_table()) {}
    static std::ctype_base::mask const* get_table() {
        static std::vector<std::ctype_base::mask> rc(table_size, std::ctype_base::mask());

        rc[','] = std::ctype_base::space;
        rc['\n'] = std::ctype_base::space;
        rc[' '] = std::ctype_base::space;
        return &rc[0];
    }
}; 
Run Code Online (Sandbox Code Playgroud)

要使用此功能,您imbue()需要具有包含此构面的区域设置的流.完成后,您可以读取数字,就好像逗号根本不存在一样.例如,我们将从输入中读取逗号分隔的数字,然后在标准输出上逐行写出:

#include <algorithm>
#include <iterator>
#include <iostream>

int main() {
    std::cin.imbue(std::locale(std::locale(), new csv_reader()));
    std::copy(std::istream_iterator<int>(std::cin), 
              std::istream_iterator<int>(),
              std::ostream_iterator<int>(std::cout, "\n"));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 恐怕此解决方案虽然不支持空字符串,但它们只会被跳过。以这个输入为例:'1,2,3,,5,6,7'。 (2认同)

小智 44

C++字符串工具箱库(Strtk)具有以下问题的解决方案:

#include <string>
#include <deque>
#include <vector>
#include "strtk.hpp"
int main()
{ 
   std::string int_string = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15";
   std::vector<int> int_list;
   strtk::parse(int_string,",",int_list);

   std::string double_string = "123.456|789.012|345.678|901.234|567.890";
   std::deque<double> double_list;
   strtk::parse(double_string,"|",double_list);

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

更多例子可以在这里找到

  • +1 - 优秀的答案. (11认同)

TC.*_*TC. 17

使用通用算法和Boost.Tokenizer的替代解决方案:

struct ToInt
{
    int operator()(string const &str) { return atoi(str.c_str()); }
};

string values = "1,2,3,4,5,9,8,7,6";

vector<int> ints;
tokenizer<> tok(values);

transform(tok.begin(), tok.end(), back_inserter(ints), ToInt());
Run Code Online (Sandbox Code Playgroud)

  • 如果你使用Boost.Tokenizer,为什么不用`boost :: lexical_cast`替换`atoi`? (14认同)

kia*_*uno 6

您还可以使用以下功能.

void tokenize(const string& str, vector<string>& tokens, const string& delimiters = ",")
{
  // Skip delimiters at beginning.
  string::size_type lastPos = str.find_first_not_of(delimiters, 0);

  // Find first non-delimiter.
  string::size_type pos = str.find_first_of(delimiters, lastPos);

  while (string::npos != pos || string::npos != lastPos) {
    // Found a token, add it to the vector.
    tokens.push_back(str.substr(lastPos, pos - lastPos));

    // Skip delimiters.
    lastPos = str.find_first_not_of(delimiters, pos);

    // Find next non-delimiter.
    pos = str.find_first_of(delimiters, lastPos);
  }
}
Run Code Online (Sandbox Code Playgroud)


Tim*_*mmm 5

这里有很多非常糟糕的答案,所以我将添加我的(包括测试程序):

#include <string>
#include <iostream>
#include <cstddef>

template<typename StringFunction>
void splitString(const std::string &str, char delimiter, StringFunction f) {
  std::size_t from = 0;
  for (std::size_t i = 0; i < str.size(); ++i) {
    if (str[i] == delimiter) {
      f(str, from, i);
      from = i + 1;
    }
  }
  if (from <= str.size())
    f(str, from, str.size());
}


int main(int argc, char* argv[]) {
    if (argc != 2)
        return 1;

    splitString(argv[1], ',', [](const std::string &s, std::size_t from, std::size_t to) {
        std::cout << "`" << s.substr(from, to - from) << "`\n";
    });

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

不错的属性:

  • 没有依赖关系(例如增强)
  • 不是疯狂的一线客
  • 容易理解(我希望)
  • 完美处理空间
  • 如果您不希望,则不分配拆分,例如,您可以使用如图所示的lambda处理它们。
  • 一次不添加一个字符-应该很快。
  • 如果使用C ++ 17,则可以将其更改为使用a std::stringview,然后它将不进行任何分配,并且应该非常快。

您可能希望更改的一些设计选择:

  • 空条目不会被忽略。
  • 一个空字符串将调用f()一次。

输入和输出示例:

""      ->   {""}
","     ->   {"", ""}
"1,"    ->   {"1", ""}
"1"     ->   {"1"}
" "     ->   {" "}
"1, 2," ->   {"1", " 2", ""}
" ,, "  ->   {" ", "", " "}
Run Code Online (Sandbox Code Playgroud)