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

问题描述 投票:75回答:15

在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 *阶梯”?

c switch-statement
15个回答
93
投票

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

// 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: */
{
}

2
投票

通常是我这样做的方式。


1
投票

这是您的操作方式。不,不是真的。


1
投票

如果它是一个2字节的字符串,您可以在这个具体示例中执行ISO639-2语言代码的操作。


0
投票

假设字节序少且sizeof(char)== 1,您可以这样做(MikeBrom建议这样做)。


0
投票

例如,函数指针是实现此目的的好方法


-2
投票

嗨,如果您遇到这种情况,这是简便快捷的方法:


38
投票

[如果您有很多情况并且不想写大量的strcmp()呼叫,则可以执行以下操作:

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

您只需要确保您的哈希函数在该字符串的可能值集中没有冲突。


32
投票

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

当然,有更有效的方法可以做到这一点。如果您对键进行排序,则可以使用二进制搜索。您也可以使用哈希表。这些事情以维护为代价而改变了您的性能。


17
投票

我这样做的首选方法是通过哈希函数(从here借用)。这使您即使在使用char *的情况下也可以利用switch语句的效率:

#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 *,并输出其结果。然后,可以像上面我一样通过宏定义这些结果。


15
投票

我认为做到这一点的最佳方法是将'识别'与功能分开:

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

}

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

5
投票

要添加到上面的Phimueme答案中,如果您的字符串始终是两个字符,则可以从两个8位字符中构建一个16位int-并将其打开(以避免嵌套的switch / case语句)。] >


5
投票

我已经发布了一个header file来对C中的字符串执行切换。它包含一组宏,这些宏隐藏对strcmp()(或类似文件)的调用,以模仿类似开关的行为。我仅在Linux中的GCC上对其进行了测试,但是我非常确定它可以进行调整以支持其他环境。


3
投票

为了将字符串与其他字符串进行比较,我们无法逃脱if-else阶梯。甚至常规的开关盒在内部也是if-else阶梯(对于整数)。我们可能只想模拟字符串的开关盒,但永远不能替换if-else阶梯。最好的字符串比较算法不能逃脱使用strcmp函数。表示逐字符比较直到找到不匹配的方法。因此,不可避免地要使用if-else阶梯和strcmp。

© www.soinside.com 2019 - 2024. All rights reserved.