Usando strtok con std :: string

Tengo una cadena que me gustaría tokenizar. Pero la strtok() C strtok() requiere que mi cadena sea un char* . ¿Cómo puedo hacer esto simplemente?

Lo intenté:

 token = strtok(str.c_str(), " "); 

que falla porque lo convierte en un const char* , no en un char*

 #include  #include  #include  int main(){ std::string myText("some-text-to-tokenize"); std::istringstream iss(myText); std::string token; while (std::getline(iss, token, '-')) { std::cout << token << std::endl; } return 0; } 

O, como se mencionó, use boost para obtener más flexibilidad.

  1. Si hay impulso disponible en su sistema (creo que es estándar en la mayoría de las distribuciones de Linux actualmente), tiene una clase Tokenizer que puede usar.

  2. De lo contrario, un Google rápido muestra un tokenizador enrollado a mano para std :: string que probablemente solo pueda copiar y pegar. Es muy corto.

  3. Y, si no te gusta ninguno de estos, entonces aquí hay una función de división () que escribí para hacerme la vida más fácil. Rompe una cuerda en pedazos usando cualquiera de los caracteres en “delim” como separadores. Las piezas se adjuntan al vector de “partes”:

     void split(const string& str, const string& delim, vector& parts) { size_t start, end = 0; while (end < str.size()) { start = end; while (start < str.size() && (delim.find(str[start]) != string::npos)) { start++; // skip initial whitespace } end = start; while (end < str.size() && (delim.find(str[end]) == string::npos)) { end++; // skip to end of word } if (end-start != 0) { // just ignore zero-length strings. parts.push_back(string(str, start, end-start)); } } } 

Duplicar la cadena, tokenizarla, luego liberarla.

 char *dup = strdup(str.c_str()); token = strtok(dup, " "); free(dup); 

Hay una solución más elegante.

Con std :: string puedes usar resize () para asignar un buffer adecuadamente grande, y & s [0] para obtener un puntero al buffer interno.

En este punto, mucha gente buena saltará y gritará en la pantalla. Pero este es el hecho. Hace aproximadamente 2 años

el grupo de trabajo de la biblioteca decidió (reunirse en Lillehammer) que al igual que para std :: vector, std :: string también debería tener formalmente, y no solo en la práctica, un buffer contiguo garantizado.

La otra preocupación es si strtok () aumenta el tamaño de la cadena. La documentación de MSDN dice:

Cada llamada a strtok modifica strToken insertando un carácter nulo después del token devuelto por esa llamada.

Pero esto no es correcto. En realidad, la función reemplaza la primera aparición de un carácter separador con \ 0. Sin cambios en el tamaño de la cuerda. Si tenemos esta cadena:

uno dos tres CUATRO

terminaremos con

one \ 0two \ 0 – three \ 0-four

Entonces mi solución es muy simple:

 std::string str("some-text-to-split"); char seps[] = "-"; char *token; token = strtok( &str[0], seps ); while( token != NULL ) { /* Do your thing */ token = strtok( NULL, seps ); } 

Lea la discusión en http://www.archivum.info/comp.lang.c++/2008-05/02889/does_std::string_have_something_like_CString::GetBuffer

EDITAR: el uso de const cast solo se usa para demostrar el efecto de strtok() cuando se aplica a un puntero devuelto por string :: c_str ().

No debe usar strtok() ya que modifica la cadena tokenizada, lo que puede provocar un comportamiento no deseado, si no es indefinido, ya que la cadena C “pertenece” a la instancia de cadena.

 #include  #include  int main(int ac, char **av) { std::string theString("hello world"); std::cout << theString << " - " << theString.size() << std::endl; //--- this cast *only* to illustrate the effect of strtok() on std::string char *token = strtok(const_cast(theString.c_str()), " "); std::cout << theString << " - " << theString.size() << std::endl; return 0; } 

Después de la llamada a strtok() , el espacio se "eliminó" de la cadena, o se redujo a un carácter no imprimible, pero la longitud permanece sin cambios.

 >./a.out hello world - 11 helloworld - 11 

Por lo tanto, debe recurrir al mecanismo nativo, la duplicación de la cadena o una biblioteca de terceros como se mencionó anteriormente.

Supongo que el lenguaje es C, o C ++ …

strtok, IIRC, reemplaza los separadores por \ 0. Eso es lo que no puede usar una cadena const. Para solucionar ese problema “rápidamente”, si la cadena no es grande, puede simplemente strdup (). Lo cual es sabio si necesitas mantener la cuerda sin alterar (lo que sugiere la const …).

Por otro lado, es posible que desee utilizar otro tokenizador, quizás rodado a mano, menos violento en el argumento dado.

Asumiendo que por “cadena” estás hablando de std :: string en C ++, podrías echarle un vistazo al paquete Tokenizer en Boost .

No funciona porque str.c_str() devuelve una cadena constante, pero char * strtok (char * str, const char * delimiters ) requiere una cadena volátil. Por lo tanto, debe usar * const_cast > inorder para que sea voletile. Te estoy dando un progtwig completo pero pequeño para tokenizar la cadena usando la función C strtok ().

 #include  #include  #include  using namespace std; int main() { string s="20#6 5, 3"; char *str=const_cast< char *>(s.c_str()); char *tok; tok=strtok(str, "#, " ); int arr[4], i=0; while(tok!=NULL){ arr[i++]=stoi(tok); tok=strtok(NULL, "#, " ); } for(int i=0; i<4; i++) cout< 

En primer lugar, yo diría usar tokenizer boost.
Alternativamente, si sus datos están separados por espacios, entonces la biblioteca de secuencias de cadenas es muy útil.

Pero los dos anteriores ya han sido cubiertos.
Entonces, como una tercera alternativa C-Like, propongo copiar el std :: string en un búfer para modificarlo.

 std::string data("The data I want to tokenize"); // Create a buffer of the correct length: std::vector buffer(data.size()+1); // copy the string into the buffer strcpy(&buffer[0],data.c_str()); // Tokenize strtok(&buffer[0]," "); 

Si no le molesta el código abierto, puede usar las clases de subflujo y subparser de https://github.com/EdgeCast/json_parser . La cadena original se deja intacta, no hay asignación ni copia de datos. No he comstackdo lo siguiente, por lo que puede haber errores.

 std::string input_string("hello world"); subbuffer input(input_string); subparser flds(input, ' ', subparser::SKIP_EMPTY); while (!flds.empty()) { subbuffer fld = flds.next(); // do something with fld } // or if you know it is only two fields subbuffer fld1 = input.before(' '); subbuffer fld2 = input.sub(fld1.length() + 1).ltrim(' ');