Análisis de cadenas en C

Se supone que es un código pedante estricto ANSI C89. Debe extraer word1, word2 y word3 de una cadena formateada [palabra1] palabra2 [ word3] y devuelve el error en cualquier otro formato.

Parece funcionar, pero parece tan feo. No es necesario comentar sobre el hecho de que GetTokenBetweenSquareBraces y GetTokenBtweenOpositeSquareBraces son duplicados.

Me encantaría recibir algunos consejos sobre cómo limpia esto.

#include <stdio.h> #include <string.h> #include <ctype.h> char * TrimWhiteSpaces(char *str) { char *out = str; int i; int len = strlen(str); for (i=0; i<len && isspace(str[i]); i++, out++); /*scan forward*/ for (i=len-1; i>=0 && isspace(str[i]); str[i]=0, i--);/*scan backward*/ return out; } char * GetTokenBetweenSquareBraces(char * input, char **output, int * output_size) { char *p = TrimWhiteSpaces(input); *output_size=0; if (p[0] == "[") { *output = TrimWhiteSpaces(p + 1); do { (*output_size)++; }while((*output)[*output_size] != "]" && isalnum((*output)[*output_size])); } else { return NULL; } return (*output) + *output_size; } char * GetTokenBtweenOpositeSquareBraces(char * input, char **output, int * output_size) { char *p = TrimWhiteSpaces(input); *output_size=0; if (p[0] == "]") { *output = TrimWhiteSpaces(p + 1); do { (*output_size)++; }while((*output)[*output_size] != "[" && isalnum((*output)[*output_size])); } else { return NULL; } return (*output) + *output_size; } int GetWords(char * str,char * word1,char * word2,char * word3) { char * next=NULL,*output=NULL; int outputsize; printf ("\nSplitting string \"%s\" into tokens:\n",str); next = GetTokenBetweenSquareBraces (str,&output,&outputsize); strncpy(word1,output,outputsize); word1[outputsize] = "\0"; strcpy(word1,TrimWhiteSpaces(word1)); if(!next) return 0; next = GetTokenBtweenOpositeSquareBraces (next,&output,&outputsize); strncpy(word2,output,outputsize); word2[outputsize] = "\0"; strcpy(word2,TrimWhiteSpaces(word2)); if(!next) return 0; next = GetTokenBetweenSquareBraces (next,&output,&outputsize); strncpy(word3,output,outputsize); word3[outputsize] = "\0"; strcpy(word3,TrimWhiteSpaces(word3)); if(!next) return 0; return 1; } void TestGetWords(char * str ) { char word1[20],word2[20],word3[20]; if (GetWords(str,word1,word2,word3)) { printf("|%s|%s|%s|\n",word1,word2,word3); } else { printf("3ViLLLL\n"); } } int main (void) { char str[] ="[ hello ] gfd [ hello2 ] "; char str2[] ="[ hello [ gfd [ hello2 ] "; char str3[] ="the wie321vg42g42g!@#"; char str4[] ="][123[]23][231["; TestGetWords(str); TestGetWords(str2); TestGetWords(str3); TestGetWords(str4); getchar(); return 1; } 

Comentarios

  • Primero, corrige tu sangría. El motor de Markdown no ‘ t como las pestañas; reemplácelas con espacios.
  • @LokiAstari: parece que reemplazó su código original.
  • Opps. Perdón. Arreglado mi error espero. Obtuve el código del artículo sobre meta. solucionó el problema de la pestaña y lo devolvió. Si esto no es correcto, lo siento, pero no puedo ‘ parecer revertir una versión.
  • @LokiAstari: Sí, se ve mucho mejor.

Respuesta

#include <stdio.h> #include <string.h> #include <ctype.h> char * TrimWhiteSpaces(char *str) { char *out = str; int i; int len = strlen(str); for (i=0; i<len && isspace(str[i]); i++, out++); /*scan forward*/ 

Yo «al menos tengo un cuerpo con un comentario aquí. Es fácil pasar por alto el punto y coma. No creo que necesites la prueba i < len. El 0 al final de la cadena debería fallar la isspace prueba, por lo que no es necesario que verifique la longitud también. Tampoco tiene sentido realizar un seguimiento de i. En su lugar, use out.

 for (i=len-1; i>=0 && isspace(str[i]); str[i]=0, i--);/*scan backward*/ 

No es realmente necesario establecer todos esos espacios en 0. En general, estás trabajando mucho en esa línea. Al menos solo deberías hacer la configuración 0 dentro del cuerpo del bucle porque no tiene nada que ver con el control del bucle.

 return out; 

Generalmente, es mejor modificar sus parámetros o devolver otros nuevos. No haga ambas cosas. Aquí devuelve un nuevo puntero de cadena y modifica la cadena original.

} char * GetTokenBetweenSquareBraces(char * input, char **output, int * output_size) { char *p = TrimWhiteSpaces(input); *output_size=0; if (p[0] == "[") { *output = TrimWhiteSpaces(p + 1); do { (*output_size)++; }while((*output)[*output_size] != "]" && isalnum((*output)[*output_size])); 

] no es un número o una letra. No necesita ambas pruebas.

 } else { return NULL; } return (*output) + *output_size; } char * GetTokenBtweenOpositeSquareBraces(char * input, char **output, int * output_size) { char *p = TrimWhiteSpaces(input); *output_size=0; if (p[0] == "]") { *output = TrimWhiteSpaces(p + 1); do { (*output_size)++; }while((*output)[*output_size] != "[" && isalnum((*output)[*output_size])); } else { return NULL; } return (*output) + *output_size; } 

¡Deja Vu! Es casi exactamente igual que la función anterior. Solo se han invertido las direcciones de los soportes. Parece que debería poder compartir ese código.

int GetWords(char * str,char * word1,char * word2,char * word3) { char * next=NULL,*output=NULL; int outputsize; printf ("\nSplitting string \"%s\" into tokens:\n",str); 

En general, recomiendo que sus funciones de trabajo no produzcan ningún resultado. También es extraña la elección de dónde poner las líneas nuevas.

 next = GetTokenBetweenSquareBraces (str,&output,&outputsize); strncpy(word1,output,outputsize); word1[outputsize] = "\0"; strcpy(word1,TrimWhiteSpaces(word1)); 

¿Por qué recorta los espacios en blanco aquí? ¿No lo hiciste ya? Estás trabajando mucho para copiar el texto. ¿Quizás eso es algo que debería haber hecho GetTokenBetweenSquareBraces?

 if(!next) return 0; next = GetTokenBtweenOpositeSquareBraces (next,&output,&outputsize); strncpy(word2,output,outputsize); word2[outputsize] = "\0"; strcpy(word2,TrimWhiteSpaces(word2)); if(!next) return 0; 

¡Deja Vu!

 next = GetTokenBetweenSquareBraces (next,&output,&outputsize); strncpy(word3,output,outputsize); word3[outputsize] = "\0"; strcpy(word3,TrimWhiteSpaces(word3)); if(!next) return 0; 

¡Deja Vu!

 return 1; } void TestGetWords(char * str ) { char word1[20],word2[20],word3[20]; 

Su código no es cuidadoso para asegurarse de no desbordar estas variables. Es posible que desee hacer algo al respecto

 if (GetWords(str,word1,word2,word3)) { printf("|%s|%s|%s|\n",word1,word2,word3); } else { printf("3ViLLLL\n"); } } int main (void) { char str[] ="[ hello ] gfd [ hello2 ] "; char str2[] ="[ hello [ gfd [ hello2 ] "; char str3[] ="the wie321vg42g42g!@#"; char str4[] ="][123[]23][231["; TestGetWords(str); TestGetWords(str2); TestGetWords(str3); TestGetWords(str4); 

Para propósitos de pruebas automatizadas, es mejor si proporciona la respuesta correcta y la contrasta en el código. De esa manera, el programa le dirá cuándo está mal.

 getchar(); return 1; 

0 se usa para indicar una ejecución exitosa del programa.

} 

En general, su programa es feo porque está usando el vocabulario incorrecto. Ha tomado el vocabulario como dado en lugar de definir el vocabulario que hizo que la tarea fuera fácil de describir. Aquí está mi enfoque de su problema

char * Whitespace(char * str) /* This function return the `str` pointer incremented past any whitespace. */ { /* when an error occurs, we return NULL. If an error has already occurred, just pass it on */ if(!str) return str; while(isspace(*str)) { str++; } return str; } char * Character(char * str, char c) /* This function tries to match a specific character. It returns `str` incremented past the character or NULL if the character was not found */ { if(!str) return str; /* Eat any whitespace before the character */ str = Whitespace(str); if(c != *str) { return NULL; } else { return str + 1; } } char * Word(char * str, char * word) /* This function reads a sequence of numbers and letter into word and then returns a pointer to the position after the word */ { /* Handle errors and whitespace */ if(!str) return str; str = Whitespace(str); /* copy characters */ while(isalnum(*str)) { *word++ = *str++; } *word = 0; /* don"t forget null!*/ return str; } int GetWords(char * str,char * word1,char * word2,char * word3) { str = Character(str, "["); str = Word(str, word1); str = Character(str, "]"); str = Word(str, word2); str = Character(str, "["); str = Word(str, word3); str = Character(str, "]"); str = Character(str, "\0"); return str != NULL; } 

What I » Lo que he hecho (o intentado hacer) es escribir las funciones de caracteres, espacios en blanco y Word de modo que sean realmente muy simples. Si comprende char *, no debería tener ningún problema con ellos. Pero estas herramientas simples se combinan muy bien para permitir una implementación sencilla de su analizador.

Comentarios

  • +1 para » Generalmente, recomiendo que sus funciones de trabajo no realicen ninguna salida «. Además, una solución muy agradable y limpia.

Respuesta

Esto es quizás un poco menos feo, pero el manejo de cadenas nunca será bonito en C.

static const char * skip_space(const char *s) { return s + strspn(s, " "); } static const char * skip_bracket(const char * s, int bracket) { s = skip_space(s); if (*s != bracket) return NULL; return skip_space(++s); } static const char * skip_word(const char * s) { return s + strcspn(s, " []"); } static const char * copy_word(char *w, const char *s, size_t size) { const char * end = skip_word(s); size_t len = end - s; if (len >= size) /* silently truncate word to buffer size */ len = size - 1; memcpy(w, s, len); w[len] = "\0"; return skip_space(end); } static int get_words(const char *s, char *w1, char *w2, char *w3, size_t size) { if ((s = skip_bracket(s, "[")) == NULL) return 0; s = copy_word(w1, s, size); if ((s = skip_bracket(s, "]")) == NULL) return 0; s = copy_word(w2, s, size); if ((s = skip_bracket(s, "[")) == NULL) return 0; s = copy_word(w3, s, size); if ((s = skip_bracket(s, "]")) == NULL) return 0; return 1; } 

Respuesta

Puede usar una máquina de estado para completar esta tarea,

#include <stdio.h> #include <string.h> void Tokenize(char* s) { // the following array return new state based on current state and current scanned char // Input: * [ ] space Print Tokenize Current State Expression /*Next state:*/char StateArray[12][3][4] = {{{11,1,11,0} ,{0,0,0,0},{0,0,0,0} }, //0 {space}*{[} {{2,11,11,1} ,{1,0,0,0},{0,0,0,0}}, //1 {space}*{char} {{2,11,4,3} ,{1,0,0,0},{0,0,1,0}}, //2 {char}*{space}?{]} {{11,11,4,3} ,{0,0,0,0},{0,0,1,0}}, //3 {space}*{]} {{5,11,11,4} ,{1,0,0,0},{0,0,0,0}}, //4 {space)*{char} {{5,7,11,6} ,{1,0,0,0},{0,1,0,0}}, //5 {char}*{space}?{[} {{11,7,11,6} ,{0,0,0,0},{0,1,0,0}}, //6 {space}*{[} {{8,11,11,7} ,{1,0,0,0},{0,0,0,0}}, //7 {space}*{char} {{8,11,10,9} ,{1,0,0,0},{0,0,1,0}}, //8 {char}*{space}?{]} {{11,11,10,9} ,{0,0,0,0},{0,0,1,0}}, //9 {space}*{]} {{11,11,11,10} ,{0,0,0,0},{0,0,0,0}}, //10 {space}* {{11,11,11,11} ,{0,0,0,0},{0,0,0,0}} }; char state=0; int len = strlen(s); for(int i =0;i<len;i++) { if(StateArray[state][1][(s[i]^91)? ((s[i]^93)?((s[i]^32)? 0:3):2):1]) printf("%c",s[i]); if(StateArray[state][2][(s[i]^91)? ((s[i]^93)?((s[i]^32)? 0:3):2):1]) printf("\n"); state=StateArray[state][0][(s[i]^91)? ((s[i]^93)?((s[i]^32)? 0:3):2):1]; switch(state) { case 11: printf("Error at column %d",i); case 10: if(i==len-1) { printf("\nParsing completed"); } } } } int main(void) { char* s= " [ word1 ] word2word [ 3 ] "; // test string Tokenize(s); } 

Comentarios

  • Hola, y Bienvenido a Code Review. Este código no es realmente una revisión. Más bien, es una forma alternativa de hacer las cosas con poca explicación sobre lo que hace, por qué funciona y por qué es mejor que el original. a través de él y preocúpese por llaves faltantes, sentencias de casos fallidas y oscuras manipulaciones bit a bit que no están documentadas. Considere agregar detalles sobre por qué esto es mejor y qué resuelve de manera diferente al OP, y por qué esas elecciones hacen que el código sea mejor.
  • ¿Creó esto a mano o hay alguna herramienta involucrada? Me gusta el concepto, pero ‘ estaría aterrorizado de apoyar esto. Hay tantos números mágicos.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *