Il modo migliore per accendere una stringa in C

In C esiste un costrutto switch che consente di eseguire rami di codice condizionali diversi in base a un valore intero di test, ad es.

 int a; /* Read the value of "a" from some source, eg user input */ switch ( a ) { case 100: // Code break; case 200: // Code break; default: // Code break; } 

Come è ansible ottenere lo stesso comportamento (cioè evitare il cosiddetto ” ifelse ladder”) per un valore stringa, ad esempio, un char * ?

    Se intendi, come scrivere qualcosa di simile a questo:

     // switch statement switch (string) { case "B1": // do something break; /* more case "xxx" parts */ } 

    Quindi la soluzione canonica in C è usare una scala if-else:

     if (strcmp(string, "B1") == 0) { // do something } else if (strcmp(string, "xxx") == 0) { // do something else } /* more else if clauses */ else /* default: */ { } 

    Se hai molti casi e non vuoi scrivere una tonnellata di chiamate strcmp() , potresti fare qualcosa del tipo:

     switch(my_hash_function(the_string)) { case HASH_B1: ... /* ...etc... */ } 

    Devi solo assicurarti che la tua funzione hash non abbia collisioni all’interno del set di possibili valori per la stringa.

    Non c’è modo di farlo in C. Ci sono molti approcci diversi. In genere, il più semplice consiste nel definire un insieme di costanti che rappresentano le stringhe e cercare una stringa per ottenere la costante:

     #define BADKEY -1 #define A1 1 #define A2 2 #define B1 3 #define B2 4 typedef struct { char *key; int val; } t_symstruct; static t_symstruct lookuptable[] = { { "A1", A1 }, { "A2", A2 }, { "B1", B1 }, { "B2", B2 } }; #define NKEYS (sizeof(lookuptable)/sizeof(t_symstruct)) int keyfromstring(char *key) { int i; for (i=0; i < NKEYS; i++) { t_symstruct *sym = lookuptable + i*sizeof(t_symstruct); if (strcmp(sym->key, key) == 0) return sym->val; } return BADKEY; } /* ... */ switch (keyfromstring(somestring)) { case A1: /* ... */ break; case A2: /* ... */ break; case B1: /* ... */ break; case B2: /* ... */ break; case BADKEY: /* handle failed lookup */ } 

    Ci sono, naturalmente, modi più efficienti per farlo. Se mantieni le tue chiavi in ​​ordine, puoi usare una ricerca binaria. Potresti usare anche una tabella hash. Queste cose cambiano le tue prestazioni a spese della manutenzione.

    Penso che il modo migliore per farlo sia il “riconoscimento” dalla funzionalità:

     struct stringcase { char* string; void (*func)(void); }; void funcB1(); void funcAzA(); stringcase cases [] = { { "B1", funcB1 } , { "AzA", funcAzA } }; void myswitch( char* token ) { for( stringcases* pCase = cases ; pCase != cases + sizeof( cases ) / sizeof( cases[0] ) ; pCase++ ) { if( 0 == strcmp( pCase->string, token ) ) { (*pCase->func)(); break; } } } 

    Il mio metodo preferito per farlo è tramite una funzione hash (presa in prestito da qui ). Ciò consente di utilizzare l’efficienza di un’istruzione switch anche quando si lavora con char *:

     #include "stdio.h" #define LS 5863588 #define CD 5863276 #define MKDIR 210720772860 #define PWD 193502992 const unsigned long hash(const char *str) { unsigned long hash = 5381; int c; while ((c = *str++)) hash = ((hash < < 5) + hash) + c; return hash; } int main(int argc, char *argv[]) { char *p_command = argv[1]; switch(hash(p_command)) { case LS: printf("Running ls...\n"); break; case CD: printf("Running cd...\n"); break; case MKDIR: printf("Running mkdir...\n"); break; case PWD: printf("Running pwd...\n"); break; default: printf("[ERROR] '%s' is not a valid command.\n", p_command); } } 

    Naturalmente, questo approccio richiede che i valori di hash per tutti i possibili caratteri accettati * vengano calcolati in anticipo. Non penso che questo sia troppo di un problema; tuttavia, poiché l'istruzione switch opera su valori fissi a prescindere. Un semplice programma può essere fatto per passare char * attraverso la funzione hash e produrre i risultati. Questi risultati possono quindi essere definiti tramite macro come ho fatto sopra.

    C’è un modo per eseguire la ricerca delle stringhe più velocemente. Presupposti: poiché stiamo parlando di un’istruzione switch, posso presumere che i valori non cambieranno durante il runtime.

    L’idea è di usare il qsort e bsearch di C stdlib.

    Lavorerò sul codice di xtofl.

     struct stringcase { char* string; void (*func)(void); }; void funcB1(); void funcAzA(); struct stringcase cases [] = { { "B1", funcB1 } , { "AzA", funcAzA } }; struct stringcase work_cases* = NULL; int work_cases_cnt = 0; // prepare the data for searching void prepare() { // allocate the work_cases and copy cases values from it to work_cases qsort( cases, i, sizeof( struct stringcase ), stringcase_cmp ); } // comparator function int stringcase_cmp( const void *p1, const void *p2 ) { return strcasecmp( ((struct stringcase*)p1)->string, ((struct stringcase*)p2)->string); } // perform the switching void myswitch( char* token ) { struct stringcase val; val.string=token; void* strptr = bsearch( &val, work_cases, work_cases_cnt, sizeof( struct stringcase), stringcase_cmp ); if (strptr) { struct stringcase* foundVal = (struct stringcase*)strptr; (*foundVal->func)(); return OK; } return NOT_FOUND; } 

    Per aggiungere alla risposta di Phimueme sopra, se la tua stringa è sempre di due caratteri, allora puoi creare un int a 16 bit tra i due caratteri a 8 bit – e accenderlo (per evitare istruzioni nidificate di switch / case).

    Questo è generalmente il modo in cui lo faccio.

     void order_plane(const char *p) { switch ((*p) * 256 + *(p+1)) { case 0x4231 : /* B1 */ { printf("Yes, order this bomber. It's a blast.\n"); break; } case 0x5354 : /* ST */ { printf("Nah. I just can't see this one.\n"); break; } default : { printf("Not today. Can I interest you in a crate of SAMs?\n"; } } } 

    Ho pubblicato un file di intestazione per eseguire lo switch sulle stringhe in C. Contiene un set di macro che nasconde la chiamata allo strcmp () (o simile) per simulare un comportamento simile a un interruttore. L’ho provato solo con GCC in Linux, ma sono abbastanza sicuro che possa essere adattato per supportare altri ambienti.

    EDIT: aggiunto il codice qui, come richiesto

    Questo è il file di intestazione che dovresti includere:

     #ifndef __SWITCHS_H__ #define __SWITCHS_H__ #include  #include  #include  /** Begin a switch for the string x */ #define switchs(x) \ { char *__sw = (x); bool __done = false; bool __cont = false; \ regex_t __regex; regcomp(&__regex, ".*", 0); do { /** Check if the string matches the cases argument (case sensitive) */ #define cases(x) } if ( __cont || !strcmp ( __sw, x ) ) \ { __done = true; __cont = true; /** Check if the string matches the icases argument (case insensitive) */ #define icases(x) } if ( __cont || !strcasecmp ( __sw, x ) ) { \ __done = true; __cont = true; /** Check if the string matches the specified regular expression using regcomp(3) */ #define cases_re(x,flags) } regfree ( &__regex ); if ( __cont || ( \ 0 == regcomp ( &__regex, x, flags ) && \ 0 == regexec ( &__regex, __sw, 0, NULL, 0 ) ) ) { \ __done = true; __cont = true; /** Default behaviour */ #define defaults } if ( !__done || __cont ) { /** Close the switchs */ #define switchs_end } while ( 0 ); regfree(&__regex); } #endif // __SWITCHS_H__ 

    Ed è così che lo usi:

     switchs(argv[1]) { cases("foo") cases("bar") printf("foo or bar (case sensitive)\n"); break; icases("pi") printf("pi or Pi or pI or PI (case insensitive)\n"); break; cases_re("^D.*",0) printf("Something that start with D (case sensitive)\n"); break; cases_re("^E.*",REG_ICASE) printf("Something that start with E (case insensitive)\n"); break; cases("1") printf("1\n"); cases("2") printf("2\n"); break; defaults printf("No match\n"); break; } switchs_end; 

    Questo è come lo fai. No, non proprio.

     #include  #include  #include  #include  #define p_ntohl(u) ({const uint32_t Q=0xFF000000; \ uint32_t S=(uint32_t)(u); \ (*(uint8_t*)&Q)?S: \ ( (S< <24)| \ ((S<<8)&0x00FF0000)| \ ((S>>8)&0x0000FF00)| \ ((S>>24)&0xFF) ); }) main (void) { uint32_t s[0x40]; assert((unsigned char)1 == (unsigned char)(257)); memset(s, 0, sizeof(s)); fgets((char*)s, sizeof(s), stdin); switch (p_ntohl(s[0])) { case 'open': case 'read': case 'seek': puts("ok"); break; case 'rm\n\0': puts("not authorized"); break; default: puts("unrecognized command"); } return 0; } 

    Se è una stringa da 2 byte, puoi fare qualcosa di simile in questo esempio concreto in cui accendo i codici lingua ISO639-2.

      LANIDX_TYPE LanCodeToIdx(const char* Lan) { if(Lan) switch(Lan[0]) { case 'A': switch(Lan[1]) { case 'N': return LANIDX_AN; case 'R': return LANIDX_AR; } break; case 'B': switch(Lan[1]) { case 'E': return LANIDX_BE; case 'G': return LANIDX_BG; case 'N': return LANIDX_BN; case 'R': return LANIDX_BR; case 'S': return LANIDX_BS; } break; case 'C': switch(Lan[1]) { case 'A': return LANIDX_CA; case 'C': return LANIDX_CO; case 'S': return LANIDX_CS; case 'Y': return LANIDX_CY; } break; case 'D': switch(Lan[1]) { case 'A': return LANIDX_DA; case 'E': return LANIDX_DE; } break; case 'E': switch(Lan[1]) { case 'L': return LANIDX_EL; case 'N': return LANIDX_EN; case 'O': return LANIDX_EO; case 'S': return LANIDX_ES; case 'T': return LANIDX_ET; case 'U': return LANIDX_EU; } break; case 'F': switch(Lan[1]) { case 'A': return LANIDX_FA; case 'I': return LANIDX_FI; case 'O': return LANIDX_FO; case 'R': return LANIDX_FR; case 'Y': return LANIDX_FY; } break; case 'G': switch(Lan[1]) { case 'A': return LANIDX_GA; case 'D': return LANIDX_GD; case 'L': return LANIDX_GL; case 'V': return LANIDX_GV; } break; case 'H': switch(Lan[1]) { case 'E': return LANIDX_HE; case 'I': return LANIDX_HI; case 'R': return LANIDX_HR; case 'U': return LANIDX_HU; } break; case 'I': switch(Lan[1]) { case 'S': return LANIDX_IS; case 'T': return LANIDX_IT; } break; case 'J': switch(Lan[1]) { case 'A': return LANIDX_JA; } break; case 'K': switch(Lan[1]) { case 'O': return LANIDX_KO; } break; case 'L': switch(Lan[1]) { case 'A': return LANIDX_LA; case 'B': return LANIDX_LB; case 'I': return LANIDX_LI; case 'T': return LANIDX_LT; case 'V': return LANIDX_LV; } break; case 'M': switch(Lan[1]) { case 'K': return LANIDX_MK; case 'T': return LANIDX_MT; } break; case 'N': switch(Lan[1]) { case 'L': return LANIDX_NL; case 'O': return LANIDX_NO; } break; case 'O': switch(Lan[1]) { case 'C': return LANIDX_OC; } break; case 'P': switch(Lan[1]) { case 'L': return LANIDX_PL; case 'T': return LANIDX_PT; } break; case 'R': switch(Lan[1]) { case 'M': return LANIDX_RM; case 'O': return LANIDX_RO; case 'U': return LANIDX_RU; } break; case 'S': switch(Lan[1]) { case 'C': return LANIDX_SC; case 'K': return LANIDX_SK; case 'L': return LANIDX_SL; case 'Q': return LANIDX_SQ; case 'R': return LANIDX_SR; case 'V': return LANIDX_SV; case 'W': return LANIDX_SW; } break; case 'T': switch(Lan[1]) { case 'R': return LANIDX_TR; } break; case 'U': switch(Lan[1]) { case 'K': return LANIDX_UK; case 'N': return LANIDX_UN; } break; case 'W': switch(Lan[1]) { case 'A': return LANIDX_WA; } break; case 'Z': switch(Lan[1]) { case 'H': return LANIDX_ZH; } break; } return LANIDX_UNDEFINED; } 

    LANIDX_ * essendo numeri interi costanti utilizzati per indicizzare negli array.

    Supponendo little endianness e sizeof (char) == 1, potresti farlo (qualcosa di simile a questo è stato suggerito da MikeBrom).

     char* txt = "B1"; int tst = *(int*)txt; if ((tst & 0x00FFFFFF) == '1B') printf("B1!\n"); 

    Potrebbe essere generalizzato per il caso BE.

    I puntatori di funzione sono un ottimo modo per farlo, ad es

    result = switchFunction(someStringKey); //result is an optional return value

    … questo chiama una funzione che hai impostato con un tasto stringa (una funzione per caso):

     setSwitchFunction("foo", fooFunc); setSwitchFunction("bar", barFunc); 

    Utilizzare un’implementazione di hashmap / table / dictionary preesistente come khash, restituire quel puntatore a una funzione all’interno di switchFunction() ed eseguirlo (o semplicemente restituirlo da switchFunction() ed eseguirlo da soli). Se l’implementazione della mappa non la memorizza, basta usare uint64_t invece di lanciare di conseguenza un puntatore.

    Non possiamo scappare if-else ladder per confrontare una stringa con gli altri. Anche la normale case-switch è anche una scala if-else (per i numeri interi) internamente. Potremmo voler solo simulare il caso di scambio per stringa, ma non possiamo mai sostituire if-else ladder. Il migliore degli algoritmi per il confronto delle stringhe non può sfuggire all’utilizzo della funzione strcmp. Mezzi per confrontare carattere per carattere fino a quando non viene trovata una mancata corrispondenza. Quindi usare if-else ladder e strcmp sono inevitabili.

    DEMO

    E qui ci sono le macro più semplici per simulare il caso di commutazione per le stringhe.

     #ifndef SWITCH_CASE_INIT #define SWITCH_CASE_INIT #define SWITCH(X) for (char* __switch_p__ = X, int __switch_next__=1 ; __switch_p__ ; __switch_p__=0, __switch_next__=1) { { #define CASE(X) } if (!__switch_next__ || !(__switch_next__ = strcmp(__switch_p__, X))) { #define DEFAULT } { #define END }} #endif 

    E tu puoi usarli come

     char* str = "def"; SWITCH (str) CASE ("abc") printf ("in abc\n"); break; CASE ("def") // Notice: 'break;' statement missing so the control rolls through subsequent CASE's until DEFAULT printf("in def\n"); CASE ("ghi") printf ("in ghi\n"); DEFAULT printf("in DEFAULT\n"); END 

    Produzione:

     in def in ghi in DEFAULT 

    Di seguito è riportato l’uso di SWITCH annidato:

     char* str = "def"; char* str1 = "xyz"; SWITCH (str) CASE ("abc") printf ("in abc\n"); break; CASE ("def") printf("in def\n"); SWITCH (str1) // < == Notice: Nested SWITCH CASE ("uvw") printf("in def => uvw\n"); break; CASE ("xyz") printf("in def => xyz\n"); break; DEFAULT printf("in def => DEFAULT\n"); END CASE ("ghi") printf ("in ghi\n"); DEFAULT printf("in DEFAULT\n"); END 

    Produzione:

     in def in def => xyz in ghi in DEFAULT 

    Ecco la stringa inversa SWITCH, dove puoi usare una variabile (piuttosto che una costante) nella clausola CASE:

     char* str2 = "def"; char* str3 = "ghi"; SWITCH ("ghi") // < == Notice: Use of variables and reverse string SWITCH. CASE (str1) printf ("in str1\n"); break; CASE (str2) printf ("in str2\n"); break; CASE (str3) printf ("in str3\n"); break; DEFAULT printf("in DEFAULT\n"); END 

    Produzione:

     in str3 

    Ciao, questo è il modo facile e veloce se hai questo caso:

    [Modalità rapida]

     int concated; char ABC[4]="";int a=1,b=4,c=2; //char[] Initializing ABC< -sprintf(ABC,"%d%d%d",a,b,c); //without space between %d%d%d printf("%s",ABC); //value as char[] is =142 concated=atoi(ABC); //result is 142 as int, not 1,4,2 (separeted) //now use switch case on 142 as an integer and all possible cases 

    [Modalità spiegata]

    ad esempio: ho molti menu, ogni scelta del primo menu porta al secondo menu, la stessa cosa con il 2 ° menu e il 3 ° menu. ma le opzioni sono diverse in modo che tu sappia che l'utente ha scelto finnaly. esempio:

    menu 1: 1 ==> menu 2: 4 ==> menu 3: 2 (...) la scelta è 142. altri casi: 111,141,131,122 ...

    sollution: memorizzare il primo 1 ° in a, 2 ° in b, 3 ° c. a = 1, b = 4, c = 2

      char ABC[4]=""; ABC< -sprintf(ABC,"%d%d%d",a,b,c); //without space between %d%d%d printf("%s",ABC); //value as char[]=142 //now you want to recover your value(142) from char[] to int as int value 142 concated=atoi(ABC); //result is 142 as int, not 1,4,2 (separeted)