Strengparsering i C

Dette skal være streng ANSI C89 pedantisk kode. Den skal trekke ut word1, word2 og word3 fra en strengformatert [word1] word2 [ word3] og returneringsfeil i noe annet format.

Det ser ut til å fungere, men det virker så stygt. Du trenger ikke å kommentere at GetTokenBetweenSquareBraces og GetTokenBtweenOpositeSquareBraces er duplikater.

Jeg vil gjerne ha noen tips om hvordan du rydde opp i dette.

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

Kommentarer

  • Rett først innrykk. Markdown-motoren ‘ liker ikke faner – erstatt dem med mellomrom.
  • @LokiAstari: det ser ut til at du byttet ut den opprinnelige koden.
  • Opps. Unnskyld. Fikset skruen min håper jeg. Jeg fikk koden fra artikkelen om meta. løst faneproblemet og satt det tilbake. Hvis dette ikke er riktig, beklager jeg, men jeg kan ‘ ikke synes å tilbakestille en versjon.
  • @LokiAstari: Jepp, ser mye bedre ut.

Svar

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

Jeg ville i det minste ha en kropp med en kommentar til den her. Det er lett å savne det semikolonet. Jeg tror ikke du trenger i < len -testen. 0 på slutten av strengen skal mislykkes i isspace -testen, så du trenger ikke å sjekke om lengden også. Det er ikke veldig fornuftig å holde rede på av i. I stedet er det bare å bruke out.

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

Det er ikke nødvendig å sette alle disse mellomrommene til 0. Samlet sett gjør du for mye arbeid i den ene linjen. Du bør i det minste bare gjøre 0-innstillingen inne i sløyfekroppen fordi den ikke har noe å gjøre med sløyfekontrollen.

 return out; 

Generelt sett er det best å enten endre parametrene eller returnere nye. Ikke gjør begge deler. Her returnerer du en ny strengpeker og endrer den originale strengen.

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

] er ikke et tall eller en bokstav. Du trenger ikke begge disse testene.

 } 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! Dette er nesten nøyaktig det samme som den forrige funksjonen. Bare brakettretningene er snudd. Det ser ut til at du burde være i stand til å dele den koden.

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

Generelt anbefaler jeg at du ikke har arbeidsfunksjonene dine til å utføre noe. Også merkelig valg av hvor du skal plassere nye linjer.

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

Hvorfor trimmer du mellomrom her? Gjorde du ikke det allerede. Du gjør mye arbeid for å kopiere teksten. Kanskje det er noe GetTokenBetweenSquareBraces burde ha gjort?

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

Koden din er ikke nøye med å sørge for at du ikke overløper disse variablene. Det kan være lurt å gjøre noe med det

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

Av hensyn til automatisert testing er det faktisk bedre hvis du gir riktig svar og sjekker mot det i koden. På den måten vil programmet fortelle deg når det er galt.

 getchar(); return 1; 

0 brukes til å indikere en vellykket programkjøring.

} 

Samlet sett er programmet stygt fordi du bruker feil ordforråd. Du har tatt ordforrådet som gitt i stedet for å definere ordforrådet som gjorde oppgaven enkel å beskrive. Her er min tilnærming til problemet ditt

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

Hva jeg » vi har gjort (eller prøvd å gjøre) er å skrive karakter-, hvitt- og ordfunksjonene slik at de er veldig enkle. Hvis du forstår char *, burde du ikke ha noen problemer med dem. Men disse enkle verktøyene kombinerer veldig pent for å muliggjøre en enkel implementering av parseren din.

Kommentarer

  • +1 for » Generelt sett anbefaler jeg at du ikke har arbeidsfunksjonene dine til å utføre «. Også veldig fin og ren løsning.

Svar

Dette er kanskje litt mindre stygt, men strenghåndtering blir aldri pen i 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; } 

Svar

Du kan bruke en tilstandsmaskin for å fullføre denne oppgaven,

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

Kommentarer

  • Hei, og velkommen til Code Review. Denne koden er egentlig ikke en anmeldelse. Snarere er det en alternativ måte å gjøre ting med liten forklaring på hva den gjør, hvorfor den fungerer, og hvorfor den er bedre enn originalen. I tillegg ser jeg ikke ut gjennom det og bekymre deg for manglende seler, fall-gjennom saksuttalelser og obskure bitvise manipulasjoner som ikke er dokumentert. Vennligst vurder å legge til detaljer om hvorfor dette er bedre, og hva det løser annerledes enn OP, og hvorfor disse valgene gir bedre kode.
  • Lagde du dette for hånd eller er det noe verktøy involvert? Jeg liker konseptet, men jeg ‘ ville være livredd for å støtte dette. Det er så mange magiske tall.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *