c - strtok null
¿Cómo se divide strtok() la cadena en tokens en C? (13)
strtok()
el funcionamiento de la strtok()
. El manual dice que divide la cadena en tokens. No puedo entender por el manual lo que realmente hace.
Añadí relojes en str
y *pch
para comprobar su funcionamiento, cuando se produjo el primer bucle while, los contenidos de str
fueron solo "esto". ¿Cómo se imprimió en la pantalla el resultado que se muestra a continuación?
/* strtok example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="- This, a sample string.";
char * pch;
printf ("Splitting string /"%s/" into tokens:/n",str);
pch = strtok (str," ,.-");
while (pch != NULL)
{
printf ("%s/n",pch);
pch = strtok (NULL, " ,.-");
}
return 0;
}
Salida:
Splitting string "- This, a sample string." into tokens: This a sample string
Aquí está mi implementación que usa la tabla hash para el delimitador, lo que significa O (n) en lugar de O (n ^ 2) (aquí hay un enlace al código) :
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define DICT_LEN 256
int *create_delim_dict(char *delim)
{
int *d = (int*)malloc(sizeof(int)*DICT_LEN);
memset((void*)d, 0, sizeof(int)*DICT_LEN);
int i;
for(i=0; i< strlen(delim); i++) {
d[delim[i]] = 1;
}
return d;
}
char *my_strtok(char *str, char *delim)
{
static char *last, *to_free;
int *deli_dict = create_delim_dict(delim);
if(!deli_dict) {
/*this check if we allocate and fail the second time with entering this function */
if(to_free) {
free(to_free);
}
return NULL;
}
if(str) {
last = (char*)malloc(strlen(str)+1);
if(!last) {
free(deli_dict);
return NULL;
}
to_free = last;
strcpy(last, str);
}
while(deli_dict[*last] && *last != ''/0'') {
last++;
}
str = last;
if(*last == ''/0'') {
free(deli_dict);
free(to_free);
deli_dict = NULL;
to_free = NULL;
return NULL;
}
while (*last != ''/0'' && !deli_dict[*last]) {
last++;
}
*last = ''/0'';
last++;
free(deli_dict);
return str;
}
int main()
{
char * str = "- This, a sample string.";
char *del = " ,.-";
char *s = my_strtok(str, del);
while(s) {
printf("%s/n", s);
s = my_strtok(NULL, del);
}
return 0;
}
Así es como implementé strtok, no tan genial, pero después de trabajar 2 horas finalmente funcionó. Admite múltiples delimitadores.
#include "stdafx.h"
#include <iostream>
using namespace std;
char* mystrtok(char str[],char filter[])
{
if(filter == NULL) {
return str;
}
static char *ptr = str;
static int flag = 0;
if(flag == 1) {
return NULL;
}
char* ptrReturn = ptr;
for(int j = 0; ptr != ''/0''; j++) {
for(int i=0 ; filter[i] != ''/0'' ; i++) {
if(ptr[j] == ''/0'') {
flag = 1;
return ptrReturn;
}
if( ptr[j] == filter[i]) {
ptr[j] = ''/0'';
ptr+=j+1;
return ptrReturn;
}
}
}
return NULL;
}
int _tmain(int argc, _TCHAR* argv[])
{
char str[200] = "This,is my,string.test";
char *ppt = mystrtok(str,", .");
while(ppt != NULL ) {
cout<< ppt << endl;
ppt = mystrtok(NULL,", .");
}
return 0;
}
La primera vez que lo llamas, proporcionas la cadena para tokenizar a strtok
. Y luego, para obtener los siguientes tokens, solo le da NULL
a esa función, siempre y cuando devuelva un puntero que no sea NULL
.
La función strtok
registra la cadena que proporcionó primero cuando la llama. (Que es realmente peligroso para aplicaciones de múltiples hilos)
Para aquellos que todavía tienen dificultades para entender esta strtok()
, eche un vistazo a este ejemplo pythontutor , es una gran herramienta para visualizar su código C (o C ++, Python ...).
En caso de que el enlace se rompiera, pega en:
#include <stdio.h>
#include <string.h>
int main()
{
char s[] = "Hello, my name is? Matthew! Hey.";
char* p;
for (char *p = strtok(s," ,?!."); p != NULL; p = strtok(NULL, " ,?!.")) {
puts(p);
}
return 0;
}
Los créditos van a Anders K.
Para entender cómo funciona strtok()
, primero se necesita saber qué es una variable estática . Este enlace lo explica bastante bien ...
La clave para la operación de strtok()
es preservar la ubicación del último separador entre llamadas seccionales (es por eso que strtok()
continúa analizando la cadena muy original que se le pasa cuando se invoca con un null pointer
en llamadas sucesivas) ..
Eche un vistazo a mi propia implementación de zStrtok()
, llamada zStrtok()
, que tiene una funcionalidad ligeramente diferente a la proporcionada por strtok()
char *zStrtok(char *str, const char *delim) {
static char *static_str=0; /* var to store last address */
int index=0, strlength=0; /* integers for indexes */
int found = 0; /* check if delim is found */
/* delimiter cannot be NULL
* if no more char left, return NULL as well
*/
if (delim==0 || (str == 0 && static_str == 0))
return 0;
if (str == 0)
str = static_str;
/* get length of string */
while(str[strlength])
strlength++;
/* find the first occurance of delim */
for (index=0;index<strlength;index++)
if (str[index]==delim[0]) {
found=1;
break;
}
/* if delim is not contained in str, return str */
if (!found) {
static_str = 0;
return str;
}
/* check for consecutive delimiters
*if first char is delim, return delim
*/
if (str[0]==delim[0]) {
static_str = (str + 1);
return (char *)delim;
}
/* terminate the string
* this assignmetn requires char[], so str has to
* be char[] rather than *char
*/
str[index] = ''/0'';
/* save the rest of the string */
if ((str + index + 1)!=0)
static_str = (str + index + 1);
else
static_str = 0;
return str;
}
Y aquí hay un ejemplo de uso
Example Usage
char str[] = "A,B,,,C";
printf("1 %s/n",zStrtok(s,","));
printf("2 %s/n",zStrtok(NULL,","));
printf("3 %s/n",zStrtok(NULL,","));
printf("4 %s/n",zStrtok(NULL,","));
printf("5 %s/n",zStrtok(NULL,","));
printf("6 %s/n",zStrtok(NULL,","));
Example Output
1 A
2 B
3 ,
4 ,
5 C
6 (null)
El código proviene de una biblioteca de procesamiento de cadenas que mantengo en Github , llamada zString. Eche un vistazo al código, o incluso contribuya :) https://github.com/fnoyanisi/zString
la función strtok runtime funciona así
la primera vez que llamas a strtok, proporcionas una cadena que quieres convertir en tokens
char s[] = "this is a string";
en el espacio de la cadena de arriba parece ser un buen delimitador entre las palabras, así que permite usar eso:
char* p = strtok(s, " ");
lo que sucede ahora es que ''s'' se busca hasta que se encuentra el carácter de espacio, se devuelve el primer token (''this'') yp apunta a ese token (cadena)
para obtener el siguiente token y continuar con la misma cadena, NULL se pasa como primer argumento, ya que strtok mantiene un puntero estático a su cadena anterior pasada:
p = strtok(NULL," ");
p ahora apunta a ''es''
y así sucesivamente hasta que no se encuentren más espacios, la última cadena se devuelve como el último token ''cadena''.
más convenientemente podrías escribirlo así para imprimir todos los tokens:
for (char *p = strtok(s," "); p != NULL; p = strtok(NULL, " "))
{
puts(p);
}
EDITAR:
Si desea almacenar los valores devueltos de strtok
, debe copiar el token a otro búfer, por ejemplo, strdup(p);
ya que la cadena original (apuntada por el puntero estático dentro de strtok
) se modifica entre iteraciones para devolver el token.
strtok () almacena el puntero en la variable estática donde la dejó la última vez, así que en su segunda llamada, cuando pase el nulo, strtok () obtiene el puntero de la variable estática.
Si proporciona el mismo nombre de cadena, nuevamente comienza desde el principio.
Además, strtok () es destructivo, es decir, realiza cambios en la secuencia original. así que asegúrese de tener siempre una copia de la original.
Un problema más de usar strtok () es que a medida que almacena la dirección en variables estáticas, en la programación multiproceso, llamar a strtok () más de una vez provocará un error. Para este uso strtok_r ().
strtok () divide la cadena en tokens. es decir, comenzar desde cualquiera del delimitador hasta el próximo sería su único token. En su caso, el token inicial será de "-" y terminará con el siguiente espacio "". Entonces, el próximo token comenzará desde "" y terminará con ",". Aquí obtienes "Esto" como salida. De manera similar, el resto de la cadena se divide en tokens de espacio en espacio y finalmente termina el último token en "."
strtok convertirá en token una cadena, es decir, la convertirá en una serie de subcadenas.
Lo hace buscando delimitadores que separen estos tokens (o subcadenas). Y especifica los delimitadores. En tu caso, quieres '''' o '','' o ''.'' o ''-'' para ser el delimitador.
El modelo de programación para extraer estos tokens es que le das a tu strtok la cadena principal y el conjunto de delimitadores. Luego lo llamas repetidas veces, y cada vez que strtok devuelva el siguiente token que encuentre. Hasta que llega al final de la cadena principal, cuando devuelve un valor nulo. Otra regla es que se pasa la cadena solo la primera vez y NULL para los tiempos subsiguientes. Esta es una forma de decirle a strtok si está comenzando una nueva sesión de tokenización con una nueva cadena, o si está recuperando tokens de una sesión de tokenización previa. Tenga en cuenta que strtok recuerda su estado para la sesión de tokenización. Y, por este motivo, no es reentrante ni seguro para subprocesos (en su lugar, debería usar strtok_r). Otra cosa que debes saber es que realmente modifica la cadena original. Escribe ''/ 0'' para los delimitadores que encuentra.
Una forma de invocar strtok, succintly, es la siguiente:
char str[] = "this, is the string - I want to parse";
char delim[] = " ,-";
char* token;
for (token = strtok(str, delim); token; token = strtok(NULL, delim))
{
printf("token=%s/n", token);
}
Resultado:
this
is
the
string
I
want
to
parse
strtok modifica su cadena de entrada. Coloca caracteres nulos (''/ 0'') en él para que devuelva bits de la cadena original como tokens. De hecho strtok no asigna memoria. Puede comprenderlo mejor si dibuja la secuencia como una secuencia de cuadros.
strtok reemplaza los caracteres en el segundo argumento con un NULL y un carácter NULL es también el final de una cadena.
strtok
mantiene una referencia interna estática apuntando al siguiente token disponible en la cadena; si le pasa un puntero NULL, funcionará desde esa referencia interna.
Esta es la razón por la cual strtok
no es reentrante; tan pronto como le pasa un nuevo puntero, esa vieja referencia interna es golpeada.
strtok
no cambia el parámetro en sí mismo ( str
). Almacena ese puntero (en una variable estática local). A continuación, puede cambiar a qué apunta ese parámetro en llamadas posteriores sin tener que volver a pasar el parámetro. (Y puede avanzar ese puntero que ha conservado, sin embargo, necesita para realizar sus operaciones).
Desde la página POSIX strtok
:
Esta función utiliza el almacenamiento estático para realizar un seguimiento de la posición actual de la cadena entre las llamadas.
Hay una variante segura para subprocesos ( strtok_r
) que no hace este tipo de magia.