Karaktersorozat elemzése a C

Állítólag ez szigorú ANSI C89 pedáns kód. Ki kell vonni word1, word2 és word3 karakterláncból, amely formázva van [word1] word2 [ word3] és a visszatérési hiba bármely más formátumban.

Úgy tűnik, hogy működik, de annyira csúnyának tűnik. Nem kell hozzászólni a GetTokenBetweenSquareBraces és a GetTokenBtweenOpositeSquareBraces duplikátumokról.

Szeretnék néhány tippet, hogyan tisztítsa meg ezt.

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

Megjegyzések

  • Először javítsa ki a behúzást. A Markdown motor nem ‘ nem szereti a füleket – helyettesítse őket szóközökkel.
  • @LokiAstari: úgy tűnik, hogy kicserélte az eredeti kódját.
  • Opps. Sajnálom. Javítottam a csavaromat remélem. A kódot a metáról szóló cikkből kaptam. kijavította a fül problémáját és visszatette. Ha ez nem helyes, sajnálom, de úgy tudom, hogy ‘ nem tűnik vissza egy verzió.
  • @LokiAstari: Igen, sokkal jobban néz ki.

Válasz

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

Legalább egy testem van egy megjegyzés itt. Könnyű kihagyni azt a pontosvesszőt. Nem hiszem, hogy szüksége lenne a i < len tesztre. A karakterlánc végén lévő 0-nak meg kell buknia a isspace teszten, ezért nem kell ellenőriznie a hosszát sem. Nincs is értelme nyomon követni i. Ehelyett csak a következőt kell használni: out.

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

Nem igazán szükséges ezeket a szóközöket 0-ra állítani. Összességében sok munkát végez ezen az egy soron. Legalább csak a 0 beállítást kell elvégeznie a hurok testén belül, mert annak semmi köze a hurok vezérléséhez.

 return out; 

Általában az a legjobb, ha módosítja a paramétereket, vagy újakat ad vissza. Ne tegye mindkettőt. Itt adhat vissza egy új karakterláncot, és módosíthatja az eredeti karakterláncot.

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

] nem szám vagy betű. Nem szükséges mindkét teszt.

 } 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! Ez majdnem pontosan megegyezik az előző funkcióval. Csak a zárójel irányai fordultak meg. Úgy tűnik, hogy képesnek kell lennie arra, hogy megossza ezt a kódot.

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

Általában azt javaslom, hogy ne működjön semmilyen kimenet. Szintén furcsa választás az új sorok helyére.

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

Miért vágja itt a szóközöket? Még nem tetted meg. Nagyon sokat dolgozol a szöveg másolásán. Lehet, hogy ezt a GetTokenBetweenSquareBrace-nek meg kellett volna tennie?

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

A kódod nem vigyáz arra, hogy megbizonyosodjon arról, hogy nem túlcsordítják ezeket a változókat. Érdemes tenned valamit ez ellen.

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

Az automatizált tesztelés szempontjából jobb, ha a helyes választ adod meg, és a kódban ellenőrizted. Így a program megmondja, ha hibás.

 getchar(); return 1; 

0 a sikeres programfuttatás jelzésére szolgál.

} 

Összességében a programod csúnya, mert rossz szókincset használsz. A szókincset megadottnak tekintette, ahelyett, hogy meghatározta volna a szókincset, amely megkönnyítette a feladat leírását. Itt állok a problémájához.

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

Amit én ” Elvégeztük (vagy megpróbáltuk) a karakter, a szóköz és a Word függvényeket úgy írni, hogy azok valóban nagyon egyszerűek legyenek. Ha megértette char *, akkor nem lehet semmi gond velük. De ezek az egyszerű eszközök nagyon szépen kombinálódnak, hogy lehetővé tegyék az elemző egyszerű végrehajtását.

Megjegyzések

  • +1 a ” kifejezéshez. Általában azt javaslom, hogy ne működjön semmilyen kimenet “. Ezenkívül nagyon szép és tiszta megoldás.

Válasz

Ez talán kicsit kevésbé csúnya, de a karakterlánc-kezelés soha nem lesz szép C-ben.

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

Válasz

Az állapotgéppel elvégezheti ezt a feladatot.

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

Megjegyzések

  • Szia, és Üdvözöljük a Code Review-nél. Ez a kód valójában nem felülvizsgálat. Inkább egy alternatív módszer a dolgok elvégzésére, kevés magyarázattal arra nézve, hogy mit csinál, miért működik és miért jobb, mint az eredeti. átgondolva, és aggódjon a hiányzó zárójelek, áteső eset-kimutatások és homályos bitenkénti manipulációk miatt, amelyeket nem dokumentáltak. Kérjük, fontolja meg részletekkel, hogy miért jobb ez, és mit old meg másként, mint az OP, és miért tesznek ezek a választások a jobb kódra.
  • Ezt kézzel hozta létre, vagy van valamilyen eszköz? Tetszik a koncepció, de ‘ rettegnék attól, hogy ezt támogatom. Annyi mágikus szám van.

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük