String-Analyse in C

Dies soll strenger ANSI C89-Pedantikcode sein. Es sollte word1, word2 und word3 aus einem mit [word1] word2 [formatierten String extrahieren word3] und Rückgabefehler in einem anderen Format.

Es scheint zu funktionieren, aber es scheint so hässlich. Keine Notwendigkeit, die Tatsache zu kommentieren, dass GetTokenBetweenSquareBraces und GetTokenBtweenOpositeSquareBraces Duplikate sind.

Ich würde gerne einige Tipps dazu erhalten Bereinigen Sie dies.

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

Kommentare

  • Korrigieren Sie zuerst Ihren Einzug. Die Markdown-Engine ‚ mag keine Tabulatoren – ersetzen Sie sie durch Leerzeichen.
  • @LokiAstari: Sie haben anscheinend seinen ursprünglichen Code ersetzt.
  • Opps. Es tut uns leid. Ich hoffe, mein Problem wurde behoben. Ich habe den Code aus dem Artikel über Meta erhalten. Das Tab-Problem wurde behoben und zurückgesetzt. Wenn dies nicht korrekt ist, tut es mir leid, aber ich kann ‚ scheinbar keine Version zurücksetzen.
  • @LokiAstari: Ja, sieht viel besser aus.

Antwort

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

Ich hätte zumindest einen Körper mit Ein Kommentar darin. Es ist leicht, dieses Semikolon zu übersehen. Ich glaube nicht, dass Sie den i < len -Test benötigen. Die 0 am Ende der Zeichenfolge sollte den isspace -Test nicht bestehen, sodass Sie nicht auch nach der Länge suchen müssen. Es ist auch nicht wirklich sinnvoll, den Überblick zu behalten von i. Verwenden Sie stattdessen einfach out.

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

Es ist nicht unbedingt erforderlich, alle diese Leerzeichen auf 0 zu setzen. Insgesamt erledigen Sie in dieser einen Zeile zu viel Arbeit. Sie sollten mindestens nur die Einstellung 0 innerhalb des Schleifenkörpers vornehmen, da dies nichts mit der Schleifensteuerung zu tun hat.

 return out; 

Im Allgemeinen ist es am besten, entweder Ihre Parameter zu ändern oder neue zurückzugeben. Tun Sie nicht beides. Hier geben Sie einen neuen Zeichenfolgenzeiger zurück und ändern die ursprüngliche Zeichenfolge.

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

] ist keine Zahl oder kein Buchstabe. Sie benötigen diese beiden Tests nicht.

 } 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! Dies ist fast genau das gleiche wie bei der vorherigen Funktion. Nur die Klammerrichtungen wurden umgekehrt. Es scheint, dass Sie in der Lage sein sollten, diesen Code freizugeben.

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

Im Allgemeinen empfehle ich, Ihre Arbeitsfunktionen nicht ausgeben zu lassen. Auch eine seltsame Auswahl, wo Zeilenumbrüche eingefügt werden sollen.

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

Warum schneiden Sie hier Leerzeichen ab? Haben Sie das nicht schon getan? Sie machen viel Arbeit, um den Text zu kopieren. Vielleicht hätte GetTokenBetweenSquareBraces das tun sollen?

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

Ihr Code achtet nicht darauf, dass diese Variablen nicht überlaufen. Möglicherweise möchten Sie etwas dagegen tun.

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

Für automatisierte Tests ist es eigentlich besser, wenn Sie die richtige Antwort angeben und dies im Code überprüfen. Auf diese Weise teilt Ihnen das Programm mit, wann es falsch ist.

 getchar(); return 1; 

0 wird verwendet, um einen erfolgreichen Programmlauf anzuzeigen.

} 

Insgesamt ist Ihr Programm hässlich, weil Sie das falsche Vokabular verwenden. Sie haben das Vokabular als gegeben genommen, anstatt das Vokabular zu definieren, das die Beschreibung der Aufgabe erleichtert hat. Hier ist meine Herangehensweise an Ihr Problem

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

Was ich “ Nachdem Sie dies getan haben (oder versucht haben), schreiben Sie die Funktionen Character, Whitespace und Word so, dass sie wirklich sehr einfach sind. Wenn Sie char * verstehen, sollten Sie keine Probleme damit haben. Diese einfachen Tools lassen sich jedoch sehr gut kombinieren, um eine einfache Implementierung Ihres Parsers zu ermöglichen.

Kommentare

  • +1 für “ Im Allgemeinen empfehle ich, dass Ihre Arbeitsfunktionen keine Ausgabe ausführen „. Auch sehr schöne und saubere Lösung.

Antwort

Dies ist vielleicht etwas weniger hässlich, Aber die Handhabung von Zeichenfolgen wird in C niemals hübsch sein.

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

Antwort

Sie können diese Aufgabe mit einer Zustandsmaschine ausführen.

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

Kommentare

  • Hallo und Willkommen bei Code Review. Dieser Code ist nicht wirklich eine Überprüfung. Vielmehr handelt es sich um eine alternative Methode, um Dinge zu erklären, die wenig erklären, was sie tun, warum sie funktionieren und warum sie besser als das Original sind. Außerdem sehe ich so aus Machen Sie sich Sorgen und machen Sie sich Sorgen über fehlende Klammern, durchfallende Fallaussagen und obskure bitweise Manipulationen, die nicht dokumentiert sind. Bitte fügen Sie Details hinzu, warum dies besser ist und was es anders als das OP löst und warum diese Auswahl zu besserem Code führt.
  • Haben Sie dies von Hand erstellt oder handelt es sich um ein Tool? Ich mag das Konzept, aber ich ‚ hätte Angst, dies zu unterstützen. Es gibt so viele magische Zahlen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.