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

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

我不想把这个问题归纳为解析其他问题。只是一个简单的逗号分隔的整数字符串,比如“1,1,1,1,2,1,1,0”。

361407 次浏览
void ExplodeString( const std::string& string, const char separator, std::list<int>& result ) {
if( string.size() ) {
std::string::const_iterator last = string.begin();
for( std::string::const_iterator i=string.begin(); i!=string.end(); ++i ) {
if( *i == separator ) {
const std::string str(last,i);
int id = atoi(str.c_str());
result.push_back(id);
last = i;
++ last;
}
}
if( last != string.end() ) result.push_back( atoi(&*last) );
}
}
std::string input="1,1,1,1,2,1,1,1,0";
std::vector<long> output;
for(std::string::size_type p0=0,p1=input.find(',');
p1!=std::string::npos || p0!=std::string::npos;
(p0=(p1==std::string::npos)?p1:++p1),p1=input.find(',',p0) )
output.push_back( strtol(input.c_str()+p0,NULL,0) );

当然,最好检查 strtol()中的转换错误。也许代码也可以从其他错误检查中获益。

一次输入一个数字,并检查下面的字符是否为 ,。如果是,则丢弃它。

#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;
}
#include <sstream>
#include <vector>


const char *input = "1,1,1,1,2,1,1,1,0";


int main() {
std::stringstream ss(input);
std::vector<int> output;
int i;
while (ss >> i) {
output.push_back(i);
ss.ignore(1);
}
}

错误的输入(例如连续的分隔符)会把这一点搞砸,但是您确实说了简单。

使用通用算法和 助推器,托克尼发射器的替代解决方案:

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());
bool GetList (const std::string& src, std::vector<int>& res)
{
using boost::lexical_cast;
using boost::bad_lexical_cast;
bool success = true;
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
boost::char_separator<char> sepa(",");
tokenizer tokens(src, sepa);
for (tokenizer::iterator tok_iter = tokens.begin();
tok_iter != tokens.end(); ++tok_iter) {
try {
res.push_back(lexical_cast<int>(*tok_iter));
}
catch (bad_lexical_cast &) {
success = false;
}
}
return success;
}

还有另外一种相当不同的方法: 使用一种特殊的区域设置,将逗号作为空白:

#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];
}
};

要使用它,您可以使用包含此方面的区域设置的 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;
}

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

更多的例子可以找到 给你

#include <sstream>
#include <vector>
#include <algorithm>
#include <iterator>


const char *input = ",,29870,1,abc,2,1,1,1,0";
int main()
{
std::stringstream ss(input);
std::vector<int> output;
int i;
while ( !ss.eof() )
{
int c =  ss.peek() ;
if ( c < '0' || c > '9' )
{
ss.ignore(1);
continue;
}


if (ss >> i)
{
output.push_back(i);
}


}


std::copy(output.begin(), output.end(), std::ostream_iterator<int> (std::cout, " ") );
return 0;
}

不那么冗长的,标准的,用逗号分开的。

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

您还可以使用以下函数。

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);
}
}
string exp = "token1 token2 token3";
char delimiter = ' ';
vector<string> str;
string acc = "";
for(int i = 0; i < exp.size(); i++)
{
if(exp[i] == delimiter)
{
str.push_back(acc);
acc = "";
}
else
acc += exp[i];
}

结构简单,适应性强,维护方便。

std::string stringIn = "my,csv,,is 10233478,separated,by commas";
std::vector<std::string> commaSeparated(1);
int commaCounter = 0;
for (int i=0; i<stringIn.size(); i++) {
if (stringIn[i] == ",") {
commaSeparated.push_back("");
commaCounter++;
} else {
commaSeparated.at(commaCounter) += stringIn[i];
}
}

最后你会得到一个字符串向量,句子中的每个元素都用空格分隔。空字符串作为单独的项保存。

基于 升级分词器的简单复制/粘贴函数。

void strToIntArray(std::string string, int* array, int array_len) {
boost::tokenizer<> tok(string);
int i = 0;
for(boost::tokenizer<>::iterator beg=tok.begin(); beg!=tok.end();++beg){
if(i < array_len)
array[i] = atoi(beg->c_str());
i++;
}

我还不能发表评论(从网站开始) ,但是在他的文章中添加了 Jerry Coffin 的奇妙的 ctype 的派生类的一个更通用的版本。

谢谢杰瑞的好主意。

(因为它必须经过同行评议,在这里添加过于临时)

struct SeparatorReader: std::ctype<char>
{
template<typename T>
SeparatorReader(const T &seps): std::ctype<char>(get_table(seps), true) {}


template<typename T>
std::ctype_base::mask const *get_table(const T &seps) {
auto &&rc = new std::ctype_base::mask[std::ctype<char>::table_size]();
for(auto &&sep: seps)
rc[static_cast<unsigned char>(sep)] = std::ctype_base::space;
return &rc[0];
}
};

我很惊讶还没有人提出使用 std::regex的解决方案:

#include <string>
#include <algorithm>
#include <vector>
#include <regex>


void parse_csint( const std::string& str, std::vector<int>& result ) {


typedef std::regex_iterator<std::string::const_iterator> re_iterator;
typedef re_iterator::value_type re_iterated;


std::regex re("(\\d+)");


re_iterator rit( str.begin(), str.end(), re );
re_iterator rend;


std::transform( rit, rend, std::back_inserter(result),
[]( const re_iterated& it ){ return std::stoi(it[1]); } );


}

此函数将所有整数插入到输入向量的后面。您可以调整正则表达式以包含负整数或浮点数等。

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

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

房子不错:

  • 没有依赖关系(例如,升级)
  • 而不是一个疯狂的俏皮话
  • 容易理解(我希望)
  • 空间处理得很好
  • 如果你不想,不会分配分割,例如,你可以用一个 lambda 来处理它们,如图所示。
  • 不会一次添加一个字符-应该很快。
  • 如果使用 C + + 17,你可以改变它使用 std::stringview,然后它不会做任何分配,应该非常快。

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

  • 不忽略空项。
  • 空字符串将调用 f ()一次。

投入和产出实例:

""      ->   {""}
","     ->   {"", ""}
"1,"    ->   {"1", ""}
"1"     ->   {"1"}
" "     ->   {" "}
"1, 2," ->   {"1", " 2", ""}
" ,, "  ->   {" ", "", " "}

这是最简单的方法,我用了很多次。它适用于任何一个字符的分隔符。

#include<bits/stdc++.h>
using namespace std;


int main() {
string str;


cin >> str;
int temp;
vector<int> result;
char ch;
stringstream ss(str);


do
{
ss>>temp;
result.push_back(temp);
}while(ss>>ch);


for(int i=0 ; i < result.size() ; i++)
cout<<result[i]<<endl;


return 0;
}