Necesito una mezcla de strtok y strtok_single

Tengo la siguiente cadena que estoy tratando de analizar para las variables.

char data[]="to=myself@gmail.com&cc=youself@gmail.com&title=&content=how are you?&signature=best regards." 

Comencé con strtok y el siguiente código

 char *to=parsePostData("to",data); char* parsePostData(char s[],char t[]) { char *postVal; char *pch; char tCpy[512];//Make a copy. Otherwise, strtok works on the char pointer, and original char array gets modified/ corrupted. strcpy(tCpy,t); pch = strtok (tCpy,"=&"); while (pch != NULL) { if(strcmp(pch,s)==0) { pch= strtok (NULL, "&"); return pch; }else{ pch = strtok (NULL, "=&"); } } } 

Esto funciona bien, excepto cuando se trata de delimitadores consecutivos como el que se encuentra después del “título”. Así que encontré esta implementación personalizada de strtok_single. Necesito saber cuándo no aparecen datos entre dos separadores de token utilizando strtok ()

 char * strtok_single (char * str, char const * delims) { static char * src = NULL; char * p, * ret = 0; if (str != NULL) src = str; if (src == NULL) return NULL; if ((p = strpbrk (src, delims)) != NULL) { *p = 0; ret = src; src = ++p; } return ret; } 

Pero con esto, el problema es que no puedo obtener la “firma”, ya que no hay un delimitador & después de eso.

¿Cómo puedo obtener una mezcla de estos dos, por lo que no me pierdo la última variable y puedo manejar delimitadores consecutivos?

Hay dos errores acechando aquí. Uno está en strtok_single() . Si lo ejecuta repetidamente, no devuelve el último segmento, después de la firma = , a diferencia de strtok() .

Cuando se soluciona, todavía hay un problema con el código en parsePostData() ; devuelve un puntero a una variable automática. La copia de la cadena debe ser manejada de manera diferente; La forma más sencilla (que es consistente con el uso de strtok() lugar de strtok_r() o strtok_s() ) es hacer que la variable tCpy estática.

Progtwig de prueba emt.c

Este es un progtwig compuesto que muestra los problemas y también un conjunto de correcciones. Aplica diferentes funciones ‘divisorias’ – funciones con la misma firma que strtok() – a los datos. Demuestra el error en strtok_single() y que strtok_fixed() corrige ese error. Demuestra que el código en parsePostData() funciona correctamente cuando se corrige y se usa strtok_fixed() .

 #include  #include  /* Function pointer for strtok, strtok_single, strtok_fixed */ typedef char *(*Splitter)(char *str, const char *delims); /* strtok_single - as quoted in SO 30294129 (from SO 8705844) */ static char *strtok_single(char *str, char const *delims) { static char *src = NULL; char *p, *ret = 0; if (str != NULL) src = str; if (src == NULL) return NULL; if ((p = strpbrk(src, delims)) != NULL) { *p = 0; ret = src; src = ++p; } return ret; } /* strtok_fixed - fixed variation of strtok_single */ static char *strtok_fixed(char *str, char const *delims) { static char *src = NULL; char *p, *ret = 0; if (str != NULL) src = str; if (src == NULL || *src == '\0') // Fix 1 return NULL; ret = src; // Fix 2 if ((p = strpbrk(src, delims)) != NULL) { *p = 0; //ret = src; // Unnecessary src = ++p; } else src += strlen(src); return ret; } /* Raw test of splitter functions */ static void parsePostData1(const char *s, const char *t, Splitter splitter) { static char tCpy[512]; strcpy(tCpy, t); char *pch = splitter(tCpy, "=&"); while (pch != NULL) { printf(" [%s]\n", pch); if (strcmp(pch, s) == 0) printf("matches %s\n", s); pch = splitter(NULL, "=&"); } } /* Fixed version of parsePostData() from SO 30294129 */ static char *parsePostData2(const char *s, const char *t, Splitter splitter) { static char tCpy[512]; strcpy(tCpy, t); char *pch = splitter(tCpy, "=&"); while (pch != NULL) { if (strcmp(pch, s) == 0) { pch = splitter(NULL, "&"); return pch; } else { pch = splitter(NULL, "=&"); } } return NULL; } /* Composite test program */ int main(void) { char data[] = "to=myself@gmail.com&cc=youself@gmail.com&title=&content=how are you?&signature=best regards."; char *tags[] = { "to", "cc", "title", "content", "signature" }; enum { NUM_TAGS = sizeof(tags) / sizeof(tags[0]) }; printf("\nCompare variants on strtok()\n"); { int i = NUM_TAGS - 1; printf("strtok():\n"); parsePostData1(tags[i], data, strtok); printf("strtok_single():\n"); parsePostData1(tags[i], data, strtok_single); printf("strtok_fixed():\n"); parsePostData1(tags[i], data, strtok_fixed); } printf("\nCompare variants on strtok()\n"); for (int i = 0; i < NUM_TAGS; i++) { char *value1 = parsePostData2(tags[i], data, strtok); printf("strtok: [%s] = [%s]\n", tags[i], value1); char *value2 = parsePostData2(tags[i], data, strtok_single); printf("single: [%s] = [%s]\n", tags[i], value2); char *value3 = parsePostData2(tags[i], data, strtok_fixed); printf("fixed: [%s] = [%s]\n", tags[i], value3); } return 0; } 

Ejemplo de salida de emt

 Compare variants on strtok() strtok(): [to] [myself@gmail.com] [cc] [youself@gmail.com] [title] [content] [how are you?] [signature] matches signature [best regards.] strtok_single(): [to] [myself@gmail.com] [cc] [youself@gmail.com] [title] [] [content] [how are you?] [signature] matches signature strtok_fixed(): [to] [myself@gmail.com] [cc] [youself@gmail.com] [title] [] [content] [how are you?] [signature] matches signature [best regards.] 

Y:

 Compare variants on strtok() ✓ strtok: [to] = [myself@gmail.com] ✓ single: [to] = [myself@gmail.com] ✓ fixed: [to] = [myself@gmail.com] ✓ strtok: [cc] = [youself@gmail.com] ✓ single: [cc] = [youself@gmail.com] ✓ fixed: [cc] = [youself@gmail.com] ✕ strtok: [title] = [content=how are you?] ✓ single: [title] = [] ✓ fixed: [title] = [] ✓ strtok: [content] = [how are you?] ✓ single: [content] = [how are you?] ✓ fixed: [content] = [how are you?] ✓ strtok: [signature] = [best regards.] ✕ single: [signature] = [(null)] ✓ fixed: [signature] = [best regards.] 

Las marcas correctas (✓ = U + 2713) e incorrectas (✕ = U + 2715) se agregaron manualmente al publicar la respuesta.

Observe cómo solo las líneas etiquetadas 'fijadas' contienen exactamente lo que se desea cada vez.

No nos ha dicho exactamente lo que quiere decir con “esto funciona bien”, aunque parece suficiente para decir que desea analizar una cadena de application/x-www-form-urlencoded . ¿Por qué no lo dijiste en primer lugar?

Considere que el primer campo, key , puede ser terminado por el primero de '=' o '&' . Sería apropiado buscar un token que termine en cualquiera de esos caracteres, para extraer la key .

El segundo campo, el value , sin embargo, no termina con un carácter '=' , por lo que no es apropiado buscar ese carácter para extraer el value . Usted querría buscar '&' solamente.

Por supuesto. Podría usar strtok para analizar esto, sin embargo, estoy seguro de que hay muchas más herramientas adecuadas. strcspn , por ejemplo, no realizará ningún cambio en los data , lo que significa que no tendrá que hacer una copia de los data como está …

 #include  #include  int main(void) { char data[]="to=myself@gmail.com&cc=youself@gmail.com&title=&content=how are you?&signature=best regards."; char *key = data; do { int key_length = strcspn(key, "&="); char *value = key + key_length + (key[key_length] == '='); int value_length = strcspn(value, "&"); printf("Key: %.*s\n" "Value: %.*s\n\n", key_length, key, value_length, value); key = value + value_length + (value[value_length] == '&'); } while (*key); return 0; }