Analizando una std :: string delimitada por comas

Si tengo una std :: cadena que contiene una lista de números separados por comas, ¿cuál es la forma más simple de analizar los números y ponerlos en una matriz de enteros?

No quiero generalizar esto para analizar nada más. Solo una simple cadena de números enteros separados por comas como “1,1,1,1,2,1,1,1,0”.

#include  #include  #include  #include  int main() { std::string str = "1,2,3,4,5,6"; std::vector vect; std::stringstream ss(str); int i; while (ss >> i) { vect.push_back(i); if (ss.peek() == ',') ss.ignore(); } for (i=0; i< vect.size(); i++) std::cout << vect.at(i)< 

Algo menos detallado, estándar y toma cualquier cosa separada por una coma.

 stringstream ss( "1,1,1,1, or something else ,1,1,1,0" ); vector result; while( ss.good() ) { string substr; getline( ss, substr, ',' ); result.push_back( substr ); } 

Otro enfoque más bien diferente: use una configuración regional especial que trate las comas como espacios en blanco:

 #include  #include  struct csv_reader: std::ctype { csv_reader(): std::ctype(get_table()) {} static std::ctype_base::mask const* get_table() { static std::vector 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]; } }; 

Para usar esto, imbue() una secuencia con una configuración regional que incluye esta faceta. Una vez que haya hecho eso, puede leer los números como si las comas no estuvieran allí. Solo por ejemplo, leeremos los números delimitados por comas de la entrada, y escribiremos una línea por cada salida estándar:

 #include  #include  #include  int main() { std::cin.imbue(std::locale(std::locale(), new csv_reader())); std::copy(std::istream_iterator(std::cin), std::istream_iterator(), std::ostream_iterator(std::cout, "\n")); return 0; } 

La biblioteca de C ++ String Toolkit (Strtk) tiene la siguiente solución a su problema:

 #include  #include  #include  #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_list; strtk::parse(int_string,",",int_list); std::string double_string = "123.456|789.012|345.678|901.234|567.890"; std::deque double_list; strtk::parse(double_string,"|",double_list); return 0; } 

Más ejemplos se pueden encontrar aquí

Solución alternativa usando algoritmos generics y 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 ints; tokenizer<> tok(values); transform(tok.begin(), tok.end(), back_inserter(ints), ToInt()); 

También podría usar la siguiente función.

 void tokenize(const string& str, vector& 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); } } 
 std::string input="1,1,1,1,2,1,1,1,0"; std::vector 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) ); 

Sería una buena idea verificar los errores de conversión en strtol() , por supuesto. Quizás el código también se pueda beneficiar de algunas otras comprobaciones de errores.

Muchas respuestas bastante terribles aquí, así que agregaré las mías (incluido el progtwig de prueba):

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

Buenas propiedades:

  • Sin dependencias (por ejemplo, impulsar)
  • No es un one-liner loco
  • Fácil de entender (espero)
  • Maneja espacios perfectamente bien
  • No asigna divisiones si no desea, por ejemplo, puede procesarlas con una lambda como se muestra.
  • No agrega caracteres de a uno por vez, debe ser rápido.
  • Si usa C ++ 17, puede cambiarlo para usar std::stringview y luego no hará ninguna asignación y será extremadamente rápido.

Algunas elecciones de diseño que quizás desee cambiar:

  • Las entradas vacías no son ignoradas.
  • Una cadena vacía llamará a f () una vez.

Ejemplo de entradas y salidas:

 "" -> {""} "," -> {"", ""} "1," -> {"1", ""} "1" -> {"1"} " " -> {" "} "1, 2," -> {"1", " 2", ""} " ,, " -> {" ", "", " "} 
 #include  #include  const char *input = "1,1,1,1,2,1,1,1,0"; int main() { std::stringstream ss(input); std::vector output; int i; while (ss >> i) { output.push_back(i); ss.ignore(1); } } 

Una mala entrada (por ejemplo, separadores consecutivos) lo estropeará, pero dijiste simple.

Me sorprende que nadie haya propuesto una solución usando std::regex todavía:

 #include  #include  #include  #include  void parse_csint( const std::string& str, std::vector& result ) { typedef std::regex_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]); } ); } 

Esta función inserta todos los enteros en la parte posterior del vector de entrada. Puede ajustar la expresión regular para incluir números enteros negativos, números de coma flotante, etc.

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

Todavía no puedo comentar (comencé en el sitio) pero agregué una versión más genérica de la clase derivada del fantástico tipo de Jerry Coffin a su publicación.

Gracias Jerry por la súper idea.

(Debido a que debe ser revisado por pares, agregándolo aquí también temporalmente)

 struct SeparatorReader: std::ctype { template SeparatorReader(const T &seps): std::ctype(get_table(seps), true) {} template std::ctype_base::mask const *get_table(const T &seps) { auto &&rc = new std::ctype_base::mask[std::ctype::table_size](); for(auto &&sep: seps) rc[static_cast(sep)] = std::ctype_base::space; return &rc[0]; } }; 
 bool GetList (const std::string& src, std::vector& res) { using boost::lexical_cast; using boost::bad_lexical_cast; bool success = true; typedef boost::tokenizer > tokenizer; boost::char_separator sepa(","); tokenizer tokens(src, sepa); for (tokenizer::iterator tok_iter = tokens.begin(); tok_iter != tokens.end(); ++tok_iter) { try { res.push_back(lexical_cast(*tok_iter)); } catch (bad_lexical_cast &) { success = false; } } return success; } 

estructura simple, fácil de adaptar, mantenimiento fácil.

 std::string stringIn = "my,csv,,is 10233478,separated,by commas"; std::vector commaSeparated(1); int commaCounter = 0; for (int i=0; i 

al final tendrá un vector de cadenas con cada elemento de la oración separado por espacios. las cadenas vacías se guardan como elementos separados.

Función simple Copiar / Pegar, basada en el tokenizador de impulso .

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

Esta es la forma más simple, que utilicé mucho. Funciona para cualquier delimitador de un solo carácter.

 #include using namespace std; int main() { string str; cin >> str; int temp; vector 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< 
 void ExplodeString( const std::string& string, const char separator, std::list& 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) ); } } 
 #include  #include  #include  #include  const char *input = ",,29870,1,abc,2,1,1,1,0"; int main() { std::stringstream ss(input); std::vector 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 (std::cout, " ") ); return 0; }