Analyse de chaînes en C

Ceci est censé être du code pédantique ANSI C89 strict. Il doit extraire word1, word2 et word3 dune chaîne au format [mot1] mot2 [ word3] et renvoie un échec dans tout autre format.

Cela semble fonctionner, mais cela semble tellement moche. Inutile de commenter le fait que GetTokenBetweenSquareBraces et GetTokenBtweenOpositeSquareBraces sont des doublons.

Jaimerais avoir quelques conseils sur la façon de nettoyez ceci.

#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; } 

Commentaires

  • Commencez par corriger votre indentation. Le moteur Markdown ne ‘ t comme les tabulations – les remplacez par des espaces.
  • @LokiAstari: il semble que vous ayez remplacé son code dorigine.
  • Opps. Pardon. Jai corrigé mon problème, jespère. Jai obtenu le code de larticle sur meta. corrigé le problème de longlet et le remettre. Si ce nest pas correct, je suis désolé, mais je ne peux ‘ sembler annuler une version.
  • @LokiAstari: Oui, cest beaucoup mieux.

Réponse

#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*/ 

Je « aurais au moins un corps avec un commentaire ici. Il est facile de rater ce point-virgule. Je ne pense pas que vous ayez besoin du test i < len. Le 0 à la fin de la chaîne doit échouer au test isspace et vous navez donc pas besoin de vérifier la longueur également. Cela na pas vraiment de sens de garder une trace sur i. À la place, utilisez simplement out.

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

Il nest pas vraiment nécessaire de mettre tous ces espaces à 0. Dans lensemble, vous faites beaucoup de travail sur cette ligne. Vous ne devriez au moins faire que le paramètre 0 à lintérieur du corps de la boucle car cela na rien à voir avec le contrôle de boucle.

 return out; 

En général, il est préférable de modifier vos paramètres ou den renvoyer de nouveaux. Ne faites pas les deux. Ici, vous retournez un nouveau pointeur de chaîne et modifiez la chaîne dorigine.

} 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])); 

] nest pas un nombre ou une lettre. Vous navez pas besoin de ces deux tests.

 } 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! Cest presque exactement la même chose que la fonction précédente. Seules les directions des supports ont été inversées. Il semble que vous devriez pouvoir partager ce code.

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 général, je déconseille de laisser vos fonctions de travail effectuer des sorties. Également un choix étrange de lemplacement des nouvelles lignes.

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

Pourquoi coupez-vous les espaces ici? Vous n’avez pas déjà fait cela. Vous faites beaucoup de travail pour copier le texte. Peut-être que cest quelque chose que GetTokenBetweenSquareBraces aurait dû faire?

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

Déjà 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]; 

Votre code ne fait pas attention à ne pas dépasser ces variables. Vous voudrez peut-être faire quelque chose à ce sujet.

 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); 

À des fins de test automatisé, il est en fait préférable de fournir la bonne réponse et de la comparer dans le code. De cette façon, le programme vous dira quand cest faux.

 getchar(); return 1; 

0 est utilisé pour indiquer une exécution réussie du programme.

} 

Dans lensemble, votre programme est moche parce que vous utilisez le mauvais vocabulaire. Vous « avez pris le vocabulaire comme donné au lieu de définir le vocabulaire qui a rendu la tâche facile à décrire. Voici mon approche de votre problème

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; } 

Ce que je » Jai fait (ou essayé de faire) décrire les fonctions Caractère, Espace blanc et Word de manière à ce quelles soient vraiment très simples. Si vous comprenez char * vous ne devriez pas avoir de problème avec eux. Mais ces outils simples se combinent très bien pour permettre une implémentation simple de votre analyseur.

Commentaires

  • +1 pour  » En règle générale, je recommande de ne pas laisser vos fonctions de travail effectuer une sortie « . En outre, une solution très agréable et propre.

Réponse

Cest peut-être un peu moins moche, mais la gestion des chaînes ne sera jamais jolie 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; } 

Réponse

Vous pouvez utiliser une machine à états pour effectuer cette tâche,

#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); } 

Commentaires

  • Bonjour, et Bienvenue dans Code Review. Ce code nest pas vraiment une révision. Il sagit plutôt dune autre façon de faire les choses avec peu dexplications sur ce quil fait, pourquoi il fonctionne et pourquoi il est meilleur que loriginal. De plus, je regarde hrough it et vous inquiétez des accolades manquantes, des déclarations de cas fall-through et des manipulations binaires obscures qui ne sont pas documentées. Veuillez envisager dajouter des détails sur les raisons pour lesquelles cela est meilleur, et ce que cela résout différemment de lOP, et pourquoi ces choix permettent un meilleur code.
  • Lavez-vous créé à la main ou y a-t-il un outil impliqué? Jaime le concept mais je ‘ serais terrifié à lidée de le soutenir. Il y a tellement de nombres magiques.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *