在C中有一个switch
构造,它允许一个人根据一个测试整数值执行不同的条件代码分支,例如,
int a;
/* Read the value of "a" from some source, e.g. user input */
switch ( a ) {
case 100:
// Code
break;
case 200:
// Code
break;
default:
// Code
break;
}
如何为字符串值获取相同的行为(即避免所谓的“if
- else
阶梯”),即char *
?
答案 0 :(得分:86)
如果你的意思是,如何写一些与此类似的东西:
// switch statement
switch (string) {
case "B1":
// do something
break;
/* more case "xxx" parts */
}
然后C中的规范解决方案是使用if-else梯形图:
if (strcmp(string, "B1") == 0)
{
// do something
}
else if (strcmp(string, "xxx") == 0)
{
// do something else
}
/* more else if clauses */
else /* default: */
{
}
答案 1 :(得分:37)
如果你有很多案例并且不想写大量的strcmp()
电话,你可以这样做:
switch(my_hash_function(the_string)) {
case HASH_B1: ...
/* ...etc... */
}
您只需要确保您的哈希函数在该字符串的可能值集合中没有冲突。
答案 2 :(得分:26)
在C中无法做到这一点。有很多不同的方法。通常,最简单的方法是定义一组表示字符串的常量,然后按字符串查找以获得常量:
#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];
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 */
}
当然,有更有效的方法可以做到这一点。如果对键进行排序,则可以使用二进制搜索。你也可以使用哈希表。这些因素会以维护为代价改变您的表现。
答案 3 :(得分:14)
我这样做的首选方法是通过哈希函数(借用here)。这使您可以利用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 *传递给hash函数并输出它们的结果。然后可以通过宏来定义这些结果,如上所述。
答案 4 :(得分:11)
我认为最好的方法是将“识别”与功能区分开来:
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;
}
}
}
答案 5 :(得分:5)
要添加上面的Phimueme的答案,如果你的字符串总是两个字符,那么你可以从两个8位字符中构建一个16位的int - 并打开它(以避免嵌套的switch / case语句)。
答案 6 :(得分:5)
有一种方法可以更快地执行字符串搜索。假设:因为我们讨论的是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;
}
答案 7 :(得分:5)
我发布了一个header file来对C中的字符串执行切换。它包含一组宏,用于隐藏对strcmp()(或类似的)的调用,以模仿类似开关的行为。 我只在Linux中使用GCC进行了测试,但我确信它可以适应其他环境。
编辑:根据要求在此处添加了代码
这是您应该包含的头文件:
#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;
答案 8 :(得分:2)
这通常是我的做法。
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";
}
}
}
答案 9 :(得分:1)
这是你如何做到的。不,不是真的。
#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;
}
答案 10 :(得分:1)
如果它是一个2字节的字符串,你可以在这个具体的例子中做一些事情,我打开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_ *是用于在数组中索引的常量整数。
答案 11 :(得分:1)
为了将字符串与其他字符串进行比较,我们无法逃脱if-else阶梯。甚至常规的开关箱在内部也是if-else阶梯(对于整数)。我们可能只想模拟字符串的开关盒,而永远不能替换if-else阶梯。最好的字符串比较算法不能逃脱使用strcmp函数。表示逐个字符比较直到找到不匹配的方法。因此,不可避免地要使用if-else阶梯和strcmp。
这是模拟字符串切换情况的最简单的宏。
#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
您可以将它们用作
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
输出:
in def
in ghi
in DEFAULT
下面是嵌套的SWITCH用法:
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
输出:
in def
in def => xyz
in ghi
in DEFAULT
这里是反向字符串SWITCH,您可以在其中使用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
输出:
in str3
答案 12 :(得分:0)
假设endianness和sizeof(char)== 1,你可以做到这一点(MikeBrom建议这样做。)
char* txt = "B1";
int tst = *(int*)txt;
if ((tst & 0x00FFFFFF) == '1B')
printf("B1!\n");
可以概括为BE案例。
答案 13 :(得分:-1)
函数指针是一种很好的方法,例如
result = switchFunction(someStringKey); //result is an optional return value
...这会调用一个由字符串键设置的函数(每个案例一个函数):
setSwitchFunction("foo", fooFunc);
setSwitchFunction("bar", barFunc);
使用预先存在的hashmap / table / dictionary实现(如khash),将该指针返回到switchFunction()
内的函数,并执行它(或者只是从switchFunction()
返回它并自己执行它)。如果地图实现没有存储它,只需使用uint64_t
而不是相应地转换为指针。
答案 14 :(得分:-2)
如果您遇到这种情况,这是简单快捷的方法:
[快速模式]
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
[解释模式]
例如: 我有很多菜单,第一个菜单上的每个选项都带你到第二个菜单, 与第二个菜单和第三个菜单相同。但选项是不同的 所以你知道用户选择了finnaly。 例如:
菜单1:1 ==&gt;菜单2:4 ==&gt;菜单3:2(...)选择是142.其他情况:111,141,131,122 ......
sollution: 存储a中的第1个,b中的第2个,c中的第3个。 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)