在C中切换string的最佳方法

在C中有一个switch结构,它可以根据testing的整数值执行不同的条件分支,例如:

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

如何获得相同的行为(即避免所谓的“ ifelse梯子”)为一个string值,即一个char *

如果你的意思是,如何写这样的东西:

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

那么C中的规范解决scheme是使用if-else梯形图:

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

如果你有很多情况,不想写strcmp()调用,你可以这样做:

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

你只需要确保你的哈希函数在string的可能值集内没有冲突。

在C中没有办法做到这一点。有很多不同的方法。 通常最简单的方法是定义一组代表你的string的常量,然后通过string查找来获得常量:

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

当然,有更有效的方法来做到这一点。 如果你保持你的密钥sorting,你可以使用二进制search。 你也可以使用哈希表。 这些东西会以维护为代价来改变性能。

我认为做到这一点的最好方法是将“识别”与function分开:

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

我这样做的首选方法是通过散列函数(从这里借用)。 这使您可以利用switch语句的效率,即使在使用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); } } 

当然,这种方法要求所有可能接受的char *的散列值都是预先计算的。 我认为这不是一个太大的问题; 但是,由于switch语句在固定值上运行,无论如何。 一个简单的程序可以通过散列函数传递char *,并输出结果。 这些结果可以通过macros来定义,就像我上面所做的那样。

要添加到上面的Phimueme的答案,如果你的string总是两个字符,那么你可以从两个8位字符中build立一个16位的int – 并打开(避免嵌套的switch / case语句)。

有一种方法可以更快地执行stringsearch。 假设:因为我们正在讨论一个switch语句,所以我可以假定这些值在运行时不会改变。

这个想法是使用C stdlib的qsort和bsearch。

我将在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; } 

这通常是我如何做到的。

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

这是你如何做到的。 不,不是。

 #include <stdio.h> #include <string.h> #include <assert.h> #include <stdint.h> #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; } 

如果它是一个2字节的string,你可以在这个具体的例子中打开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_ *是用于在数组中进行索引的常量整数。

我已经发布了一个头文件来执行C中的string切换。它包含一组隐藏对strcmp()(或类似的)的调用以模仿类似开关行为的macros。 我已经在Linux中使用GCC进行了testing,但是我确信它可以适应其他环境。

编辑:在这里添加代码,按要求

这是你应该包括的头文件:

 #ifndef __SWITCHS_H__ #define __SWITCHS_H__ #include <string.h> #include <regex.h> #include <stdbool.h> /** 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__ 

这就是你如何使用它:

 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; 

假设little endianness和sizeof(char)== 1,你可以做到这一点(MikeBrombuild议的)。

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

这可以推广到BE的情况。

那么这与我一起工作。 方法是,你正在采取第一个字母,并采取切换情况下的其余字母。

 #include<stdio.h> #include<string.h> int main() { char str[10];//this is the string we are taking scanf("%s",str); switch(str[0]) case str[1]: if(str[2] && str[3]) { printf("Your condition"); } else { printf("not expected string"); } } 

那么如果你有一个string大于3或更多的字符,请考虑在if条件中使用strstr例如:

 switch(str[0]) case str[1]: if(strstr(str,"req"))//similar to string comparision { printf("required condition"); } 

如果你有这种情况,这是简单而快速的方法:

[快速模式]

 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 

[解释模式]

例如:我有很多菜单,第一个菜单上的每个选项都会将您带到第二个菜单,第二个菜单和第三个菜单也是如此。但是选项是不同的,因此您知道用户已经select了finnaly。 例如:

菜单1:1 ==>菜单2:4 ==>菜单3:2(…)的select是142.其他情况下:111,141,131,122 …

溶剂:存储第一个1,第二个b,第三个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)