Tengo la siguiente cadena que estoy tratando de analizar en busca de variables.
char data[]="to=myself@gmail.com&cc=youself@gmail.com&title=&content=how are you?&signature=best regards."
Empecé 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 está después de “título”. Así que encontré esta implementación personalizada de strtok_single. Necesita saber cuándo no aparecen datos entre dos separadores de tokens usando 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 “firma”, ya que no hay un delimitador & después de eso.
¿Cómo puedo obtener una combinación de estos dos, para no perderme la última variable y poder manejar delimitadores consecutivos?
Hay dos errores al acecho aquí. uno está en strtok_single()
. Si lo ejecuta repetidamente, no devuelve el último segmento, después del =
después de la firma, a diferencia strtok()
.
Cuando eso se solucione, 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 manejarse de manera diferente; la forma más sencilla (que es coherente con el uso strtok()
en vez de strtok_r()
o strtok_s()
) es hacer la tCpy
estática variable.
Programa de prueba emt.c
Este es un programa compuesto que muestra los problemas y también un conjunto de correcciones. Aplica diferentes funciones ‘divisoras’, funciones con la misma firma que strtok()
— a los datos. Demuestra el error en strtok_single()
y eso strtok_fixed()
corrige ese error. Demuestra que el código en parsePostData()
funciona correctamente cuando está arreglado y strtok_fixed()
se usa
#include <stdio.h>
#include <string.h>
/* 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;
}
Salida de ejemplo de emt
Compare variants on strtok()
strtok():
[to]
[myself@gmail.com]
[cc]
[youself@gmail.com]
I need a mix of strtok and strtok_single
[content]
[how are you?]
[signature]
matches signature
[best regards.]
strtok_single():
[to]
[myself@gmail.com]
[cc]
[youself@gmail.com]
I need a mix of strtok and strtok_single
[]
[content]
[how are you?]
[signature]
matches signature
strtok_fixed():
[to]
[myself@gmail.com]
[cc]
[youself@gmail.com]
I need a mix of strtok and strtok_single
[]
[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: I need a mix of strtok and strtok_single = [content=how are you?]
✓ single: I need a mix of strtok and strtok_single = []
✓ fixed: I need a mix of strtok and strtok_single = []
✓ 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 como ‘fixed’ contienen exactamente lo que se desea cada vez.
No nos ha dicho exactamente lo que quiere decir con “esto funciona bien”, aunque parece suficiente decir que desea analizar un application/x-www-form-urlencoded
cuerda. ¿Por qué no lo dijiste en primer lugar?
Considere que el primer campo, key
puede ser terminado por el primero de cualquiera '='
o '&'
. Sería apropiado buscar un token que termine en cualquiera de esos caracteres, para extraer key
.
El segundo campo, value
sin embargo, no es terminado por un '='
carácter, por lo que es inapropiado buscar ese carácter para extraer value
. Querrás buscar '&'
solamente.
Por supuesto. Tú pudo utilizar 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 data
lo que significa que no necesitará hacer una copia de data
como tu eres…
#include <stdio.h>
#include <string.h>
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;
}
Tal vez mire stackoverflow.com/questions/16807188/strtok-analogue-in-c?rq=1. Esto se dividirá en “=&” y “&” en una sola pasada, aunque supongo que obtendrá un falso espacio en blanco en “=&”, si entiendo lo que están haciendo correctamente.
– mtrw
18 de mayo de 2015 a las 2:14
@mtrw Estoy trabajando en arduino IDE. Si es posible, me gustaría no agregar más bibliotecas por cuestiones de tamaño del programa. Espero que haya una manera de modificar el código anterior para obtener el resultado, pero si no, buscaré el método que sugirió.
– aVC
18 de mayo de 2015 a las 2:21
@Cicada Estoy usando Arduino IDE y soy un principiante. Creo que usa C,C++ stackoverflow.com/a/11813275/903978
– aVC
18 de mayo de 2015 a las 2:29
@ Cicada, C sería mejor. Gracias 🙂
– aVC
18 de mayo de 2015 a las 2:36
Usé este y me funciona. stackoverflow.com/a/3375658/903978
– aVC
18 de mayo de 2015 a las 3:26