Analizarea șirurilor în C

Se presupune că acesta este un cod pedantic ANSI C89 strict. Ar trebui să extragă word1, word2 și word3 dintr-un șir formatat [word1] word2 [ word3] și returnarea eșecului în orice alt format.

Se pare că funcționează, dar pare atât de urât. Nu este nevoie să comentați faptul că GetTokenBetweenSquareBraces și GetTokenBtweenOpositeSquareBraces sunt duplicate.

Mi-ar plăcea câteva sfaturi despre cum să curățați acest lucru.

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

Comentarii

  • În primul rând, remediați indentarea. Motorul Markdown nu ‘ este ca filele – înlocuiți-le cu spații.
  • @LokiAstari: se pare că i-ați înlocuit codul original.
  • Opps. Îmi pare rău. Sper că am reparat șurubul. Am primit codul din articolul de pe meta. a remediat problema filelor și a pus-o înapoi. Dacă acest lucru nu este corect, îmi pare rău, dar nu pot ‘ să par să retrag o versiune.
  • @LokiAstari: Da, arată mult mai bine.

Răspuns

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

Aș avea cel puțin un corp cu un comentariu aici. Este ușor să ratezi punctul și virgula. Nu cred că ai nevoie de testul i < len. 0 de la sfârșitul șirului ar trebui să eșueze testul isspace și, prin urmare, nu trebuie să verificați și lungimea. De asemenea, nu are sens să țineți evidența. din i. În schimb, folosiți doar out.

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

Nu este cu adevărat necesar să setați toate acele spații la 0. În general, lucrați mult în acea linie. Ar trebui să faceți cel puțin setarea 0 în interiorul corpului buclei, deoarece nu are nicio legătură cu controlul buclei.

 return out; 

În general, este bine să vă modificați parametrii sau să le întoarceți pe noi. Nu faceți ambele. Aici reveniți un nou indicator de șir și modificați șirul 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])); 

] nu este un număr sau o literă. Nu aveți nevoie de ambele teste.

 } 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! Acest lucru este aproape exact la fel ca funcția anterioară. Doar direcțiile parantezei au fost inversate. Se pare că ar trebui să puteți împărtăși codul respectiv.

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

În general, vă recomand să nu faceți funcțiile dvs. de lucru să facă orice ieșire. De asemenea, o alegere ciudată de unde să puneți linii noi.

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

De ce tăiați spațiul alb aici? Nu ați făcut deja asta. Faceți o mulțime de lucruri pentru a copia textul. Poate că asta ar fi trebuit să fie făcut de 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]; 

Codul dvs. nu este atent să vă asigurați că nu depășiți aceste variabile. Poate doriți să faceți ceva în legătură cu asta

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

În scopul testării automate, este mai bine dacă furnizați răspunsul corect și verificați în cod. În acest fel, programul vă va spune când este greșit.

 getchar(); return 1; 

0 este utilizat pentru a indica o rulare reușită a programului.

} 

În general, programul dvs. este urât, deoarece utilizați un vocabular greșit. Ați „luat vocabularul ca fiind dat în loc să definiți vocabularul care a făcut sarcina ușor de descris. Iată abordarea mea asupra problemei dvs.

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 eu” Am făcut (sau am încercat să fac) este să scriu funcțiile Character, Whitespace și Word astfel încât să fie într-adevăr foarte simple. Dacă înțelegeți char * nu ar trebui să aveți probleme cu ele. Dar aceste instrumente simple se combină foarte bine pentru a permite o implementare simplă a analizorului dvs.

Comentarii

  • +1 pentru ” În general, vă recomand să nu faceți funcțiile dvs. de lucru să efectueze orice ieșire „. De asemenea, o soluție foarte drăguță și curată.

Răspuns

Acesta este poate puțin mai puțin urât, dar manevrarea șirurilor nu va fi niciodată frumoasă în 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ăspuns

Puteți utiliza o mașină de stare pentru a finaliza această sarcină,

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

Comentarii

  • Bună ziua și bine ați venit la Revizuirea codului. Acest cod nu este cu adevărat o recenzie. Mai degrabă este un mod alternativ de a face lucrurile, cu puține explicații cu privire la ceea ce face, de ce funcționează și de ce este mai bun decât originalul. prin el și vă faceți griji cu privire la lipsa acoladelor, declarații minuscule de caz și manipulări obscure pe biți care nu sunt documentate. Vă rugăm să luați în considerare adăugarea de detalii cu privire la motivul pentru care acest lucru este mai bun și la ce se rezolvă diferit față de PO și de ce aceste alegeri fac un cod mai bun.
  • Ați creat acest lucru manual sau există vreun instrument implicat? Îmi place conceptul, dar ‘ aș fi îngrozit să susțin acest lucru. Există atât de multe numere magice.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *