在C中打开字符串的最佳方法

Nik*_*las 74 c switch-statement

在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;
}
Run Code Online (Sandbox Code Playgroud)

如何为字符串值获得相同的行为(即避免所谓的" if- else阶梯"),即char *

Bar*_*nau 90

如果你的意思是,如何编写与此类似的东西:

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

然后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: */
{
}
Run Code Online (Sandbox Code Playgroud)

  • @Niklas:定义不是字符串.如果define是一个数字,你可以直接在你的开关中使用它,如`switch(something){case A:/*...*/ break; 案例B:/*...*/ break; }`. (5认同)
  • @Niklas:你应该澄清一下你的问题:"B1"和"B2"究竟是一个int的特例吗? (3认同)
  • @Niklas:这是您问题的重要信息.你能更新你的问题并解释(如果可能的话,用一些(伪)代码)你想要做什么? (2认同)

Edg*_*net 38

如果你有很多案例并且不想写大量的strcmp()电话,你可以这样做:

switch(my_hash_function(the_string)) {
    case HASH_B1: ...
    /* ...etc... */
}
Run Code Online (Sandbox Code Playgroud)

您只需确保您的哈希函数在字符串的可能值集合内没有冲突.

  • @ArunSaha:显然不是这些角色的任意组合. (7认同)
  • "确保你的哈希函数在字符串的可能值集合中没有冲突." - 字母"[a-zA-Z0-9_]"是否存在这样的哈希函数?任何例子? (4认同)
  • 如果使用固定长度的字符串键,则可以将它们各自转换为唯一的整数;没有碰撞的可能。 (2认同)

pli*_*nth 31

在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 */
}
Run Code Online (Sandbox Code Playgroud)

当然,有更有效的方法可以做到这一点.如果对键进行排序,则可以使用二进制搜索.你也可以使用哈希表.这些因素会以维护为代价改变您的表现.

  • 更好地使用枚举而不是一组#defines作为键,但除此之外你可以做的最好. (4认同)
  • @asdf这就是指针算术在c中的工作原理。sizeof 是隐式的。 (2认同)

小智 17

我这样做的首选方法是通过哈希函数(从这里借用).这使您可以利用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,这种方法要求提前计算所有可能接受的char*的哈希值.我不认为这是一个太大的问题; 但是,因为switch语句无论如何都在固定值上运行.可以通过一个简单的程序将char*传递给hash函数并输出它们的结果.然后可以通过宏来定义这些结果,如上所述.

  • *这使您即使在使用 char * 时也可以利用 switch 语句的效率* 除非字符串调用,否则与“strcmp()”调用的“if-else if...”梯形图相比,效率不会提高太多被比较的包含相对较长的共同初始字符序列。如果字符串的前 4-8 个字符都不同,则正确优化的“strcmp()”将通过单个操作来比较字符串,而使用哈希函数时,每次都必须对整个字符串进行哈希处理。 (2认同)

xto*_*ofl 14

我认为最好的方法是将"识别"与功能区分开来:

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

}
Run Code Online (Sandbox Code Playgroud)


Dar*_*usz 6

有一种方法可以更快地执行字符串搜索.假设:因为我们讨论的是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;
}
Run Code Online (Sandbox Code Playgroud)


小智 5

要添加上面的Phimueme的答案,如果你的字符串总是两个字符,那么你可以从两个8位字符中构建一个16位的int - 并打开它(以避免嵌套的switch/case语句).

  • @Onion:你会注意到MikeBrom目前没有声称对自己以外的帖子发表评论并回答他自己的问题.也就是说,@ Mike"over"在SO中很滑,因为没有可靠的排序.最好链接到答案,如*"...... [在Phimueme的回答中](http://stackoverflow.com/questions/4014827/best-way-to-switch-on-a-string-in-c/4014887# 4014887)..."*(虽然现在删除了答案,但该链接仅适用于具有10k +声誉的用户). (2认同)

And*_*ron 5

我已经发布了一个头文件来执行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__
Run Code Online (Sandbox Code Playgroud)

这就是你如何使用它:

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;
Run Code Online (Sandbox Code Playgroud)