Analisi delle stringhe in C

Questo dovrebbe essere un rigoroso codice ANSI C89 pedante. Dovrebbe estrarre word1, word2 e word3 da una stringa formattata [parola1] parola2 [ word3] e restituisce il fallimento in qualsiasi altro formato.

Sembra funzionare, ma sembra così brutto. Non cè bisogno di commentare il fatto che GetTokenBetweenSquareBraces e GetTokenBtweenOpositeSquareBraces sono duplicati.

Mi piacerebbe alcuni suggerimenti su come ripulisci.

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

Commenti

  • Prima di tutto correggi il rientro. Il motore Markdown non ‘ ama le tabulazioni: sostituiscile con spazi.
  • @LokiAstari: sembra che tu abbia sostituito il suo codice originale.
  • Opps. Scusate. Ho risolto il mio errore, spero. Ho ricevuto il codice dallarticolo su meta. risolto il problema della scheda e rimetterlo. Se ciò non è corretto, mi dispiace, ma posso ‘ non sembra ripristinare una versione.
  • @LokiAstari: Sì, sembra molto meglio.

Risposta

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

Ho “d almeno un corpo con un commento qui. È facile perdere il punto e virgola. Non credo che tu abbia bisogno del test i < len. Lo 0 alla fine della stringa non dovrebbe superare il test isspace e quindi non è necessario controllare anche la lunghezza. Inoltre non ha molto senso tenerne traccia di i. Utilizza invece out.

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

Non è realmente necessario impostare tutti quegli spazi su 0. Nel complesso, stai facendo molto lavoro in quella riga. Dovresti almeno impostare solo 0 allinterno del corpo del ciclo perché non ha nulla a che fare con il controllo del ciclo.

 return out; 

In genere, è meglio modificare i parametri o restituirne di nuovi. Non fare entrambe le cose. Qui restituisci un nuovo puntatore di stringa e modifichi la stringa originale.

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

] non è un numero o una lettera. Non sono necessari entrambi questi test.

 } 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! Questa è quasi esattamente la stessa della funzione precedente. Solo le direzioni della staffa sono state invertite. Sembra che dovresti essere in grado di condividere quel codice.

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

In genere sconsiglio che le tue funzioni di lavoro eseguano alcun output. Strana anche la scelta di dove inserire le nuove righe.

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

Perché tagli gli spazi qui? Non lhai già fatto. Stai facendo molto lavoro per copiare il testo. Forse è qualcosa che GetTokenBetweenSquareBraces avrebbe dovuto fare?

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

Già visto!

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

Il tuo codice non sta “attento ad essere sicuro di non sovraccaricare queste variabili. Potresti voler fare qualcosa al riguardo

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

Per scopi di test automatizzati, in realtà è meglio se fornisci la risposta corretta e confrontala con quella nel codice. In questo modo il programma ti dirà quando è sbagliato.

 getchar(); return 1; 

0 viene utilizzato per indicare lesecuzione del programma con successo.

} 

Nel complesso, il tuo programma è brutto perché stai usando il vocabolario sbagliato. Hai preso il vocabolario come dato invece di definire il vocabolario che ha reso il compito facile da descrivere. Ecco il mio approccio al tuo 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; } 

Quello che io ” quello che abbiamo fatto (o provato a fare) è stato scrivere le funzioni Carattere, Spazio vuoto e Word in modo che siano davvero molto semplici. Se comprendi char * non dovresti “avere problemi con loro. Ma questi semplici strumenti si combinano molto bene per consentire unimplementazione diretta del tuo parser.

Commenti

  • +1 per ” Generalmente sconsiglio che le tue funzioni di lavoro eseguano qualsiasi output “. Inoltre, una soluzione molto bella e pulita.

Risposta

Questo è forse un po meno brutto, ma la gestione delle stringhe non sarà mai carina in 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; } 

Risposta

Puoi utilizzare una macchina a stati per completare questa attività,

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

Commenti

  • Ciao e benvenuto in Code Review. Questo codice non è realmente una recensione. Piuttosto, è un modo alternativo di fare le cose con poche spiegazioni su cosa fa, perché funziona e perché è migliore delloriginale. Inoltre, mi sembra di attraverso di esso e preoccuparsi della mancanza di parentesi graffe, delle dichiarazioni casuali casuali e di oscure manipolazioni bit per bit che non sono documentate. Si prega di considerare laggiunta di dettagli sul motivo per cui questo è migliore e su cosa risolve in modo diverso rispetto allOP e perché queste scelte rendono il codice migliore.
  • Lhai creato a mano o cè qualche strumento coinvolto? Mi piace il concetto ma ‘ sarei terrorizzato allidea di sostenerlo. Ci sono così tanti numeri magici.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *