将char *转换为枚举值的有效方法

|| 在C语言中,我可以使用预处理器将ѭ0转换为等效的字符串。 但是,有什么巧妙的技巧可以将
char*
转换为
enum
。 我可以为每个字符串使用if语句和
strcmp
,并返回等效的
enum
,但是还有一种更优雅的方法吗?     
已邀请:
        请不要做这种黑客。您几乎可以肯定存在设计缺陷。 编辑:如果由于某些原因您确实必须这样做,那么我会一起砍掉这个。该示例还应显示其用法:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/*** BEGIN HACK ***/

#define CREATEENUM(name, first, ...) \\
    typedef enum { first = 0, __VA_ARGS__ } name; \\
    char name##_s[] = #first \", \" #__VA_ARGS__;
#define TOSTR(x) #x
#define TOENUM(name, x) ((name) _toenum(name##_s, x))

long _toenum(char *enum_s, const char *x) {
    long i = 0;
    size_t len = strlen(enum_s);

    char *copy = (char*) malloc(sizeof(char) * (len + 1));
    strncpy(copy, enum_s, len);
    copy[len] = \'\\0\';

    char *saveptr = NULL;
    char *s = strtok_r(copy, \", \", &saveptr);
    do {
        if (strcmp(s, x) == 0) {
            free(copy);
            return i;
        }
        i++;
    } while((s = strtok_r(NULL, \", \", &saveptr)) != NULL);

    free(copy);
    return -1;
}

/*** END HACK ***/

// create enum with the name \"super\"
CREATEENUM(super,
    COOL,
    AWESOME,
    UBER,
    JON_SKEET
)

int main(int argc, char *argv[]) {
    printf(\"%d\\n\", TOENUM(super, \"JON_SKEET\")); // 3
    printf(\"%d\\n\", TOENUM(super, \"EXTREME\")); // -1 (not found)
    printf(\"%d\\n\", TOENUM(super, \"COOL\")); // 0

    printf(\"%s\\n\", TOSTR(AWESOME)); // AWESOME
    return 0;
}
    
        您可以使用enum和char *值创建一个结构,并在每次转换中搜索它们。如果您有很多枚举类型,可能会很方便。基于此线程。
enum colors {
  unknown = 0,
  red,
  blue,
  black,
  yellow
};

struct enumtypes
{
   colors color;
   char* str;
};

struct enumtypes array[] = {
  {red,\"red\"},
  {blue,\"blue\"}
  // etc for each enum type  
};

// function to convert string to enum type
colors cvt(const char* str)
{
   const int sz = sizeof(array) / sizeof(array[0]);

   for(int i = 0; i < sz; i++)
   {
       if(strcmp(array[i].str, str) == 0)
          return array[i].color;
   }
   return unknown;
}
    
        我怀疑您真正想要的不是枚举,而是一种将整数映射到字符串并返回的方法。例如,您可能表示月份,并使用整数来进行简单比较(\“此日期早于该日期\”),并使用字符串来进行更人性化的表示。 (由于您的问题并未解释您实际上要解决的问题,因此我不得不猜测。) 如果放弃枚举,则可以编写一个小帮助程序库,以一般方式解决此问题。实现映射的一种非常简单的方法是使用简单的字符串数组,并将数组中的索引用作整数:
#include <string.h>

/* Find string in mapping. Return -1 if not found. */
int map_string_to_int(char *map[], int count, char *string)
{
    for (int i = 0; i < count; ++i) {
        if (strcmp(map[i], string) == 0)
            return i;
    }
    return -1;
}

/* Map int to string. Return NULL if not found. */
char *map_int_to_string(char *map[], int count, int i)
{
    if (i < 0 || i >= count)
        return NULL;
    return map[i];
}

/* Add new string to mapping. Return its unique id. Return existing id if
   string was in mapping already. Caller is responsible to make sure 
   there\'s room. Caller is responsible for making sure string does not
   get de-allocated. */
int map_add(char *map[], int *count, char *string)
{
    int i;

    i = map_string_to_int(map, *count, string);
    if (i == -1) {
        i = *count;
        map[i] = string;
        ++(*count);
    }
    return i;
}


/* A main program for testing, not actually part of the library. */

#include <stdio.h>

#define N 1024
int main(void)
{
    char *map[N];
    int count;

    map_add(map, &count, \"Monday\");
    map_add(map, &count, \"Tuesday\");
    map_add(map, &count, \"Thursday\");
    map_add(map, &count, \"Wednesday\");
    map_add(map, &count, \"Friday\");
    map_add(map, &count, \"Sunday\");
    map_add(map, &count, \"Saturday\");

    for (int i = 0; i < count; ++i)
        printf(\"[%d] = %s\\n\", i, map_int_to_string(map, count, i));

    printf(\"Monday is %d\\n\", map_string_to_int(map, count, \"Monday\"));

    return 0;
}
可能有更有效的方法。在开始使用它们之前,请记住要测量此映射对运行时的实际影响。 有第二个数组,它使字符串保持排序,并使用以下命令查找字符串 二进制搜索。第一张表仍然存在,因此映射整数仍然很快 到一个字符串。 将哈希表用于第二个数组。 如果您完全放弃第一个数组,并使用哈希值作为整数 可以找到一个对您的特定数据没有冲突的哈希函数。这应该 如果您无论如何都要使用枚举,那将是完全可行的,此后 一组值在编译时是固定的。 (您仍然需要哈希表进行反向 映射:从整数到字符串。) 例如,如果您的值表示工作日,则使用前两个字符作为哈希函数就足够了。有关在一般情况下构造此类哈希函数的一些相关信息的指针,请参见http://en.wikipedia.org/wiki/Perfect_hashing。     
        如果您可以信任ѭ1,并且可以进行简单的计算,请尝试以下操作:
#include <stdio.h>

enum Weekdays {
      Sun = \'S\' + \'u\' + \'n\',
      Mon = \'M\' + \'o\' + \'n\',
      Tue = \'T\' + \'u\' + \'e\',
      Wed = \'W\' + \'e\' + \'d\',
      Thu = \'T\' + \'h\' + \'u\',
      Fri = \'F\' + \'r\' + \'i\',
      Sat = \'S\' + \'a\' + \'t\'
};

int main(void) {
  char tmp[10];
  printf(\"Enter day of the week: \");
  fflush(stdout);
  if (fgets(tmp, sizeof tmp, stdin)) {

    enum Weekdays enumvalue =
          tmp[0] + tmp[1] + tmp[2];

    switch (enumvalue) {
      default:    printf(\"Ohoh\\n\"); break;
      case Sun:   printf(\"Sun: %d\\n\", Sun); break;
      case Mon:   printf(\"Mon: %d\\n\", Mon); break;
      case Tue:   printf(\"Tue: %d\\n\", Tue); break;
      case Wed:   printf(\"Wed: %d\\n\", Wed); break;
      case Thu:   printf(\"Thu: %d\\n\", Thu); break;
      case Fri:   printf(\"Fri: %d\\n\", Fri); break;
      case Sat:   printf(\"Sat: %d\\n\", Sat); break;
    }
  }
  return 0;
}
    
        不是很聪明,但是如果您的字符串很少且很短(4个字节或某种适合整数类型的字符串),则可以将它们重铸为整数并在switch等中使用。黑客当然可以,但有时可能会给您您想要的东西。     
        没有任何“聪明的”方法可以有效地做到这一点。只需为您的特定枚举编写一个简单的解析器即可完成。当然,存在局限性,即这仅适用于单个枚举,并且通常不适用于所有枚举。 C没有像其他语言一样具备执行此操作的机制,它的级别太低。 对于此问题,下面是一个手写的“古典” DFA解析此
MyEnum
的示例:
typedef enum
{
    MyEnum_foo,
    MyEnum_bar,
    MyEnum_baz,
} MyEnum;

/**
 * M -> y -> E -> n -> u -> m -> _ -> f -> o -> o
 *                                 -> b -> a -> r
 *                                           -> z
 */

MyEnum parse_MyEnum(const char *str)
{
    int state = 0;
    MyEnum result;
    if (str == 0) { /* handle null pointer error */ }
    for ( ; ; )
    {
        char c = *str++;
        switch (state)  /* case sensitive parse */
        {
        case 0:
            /* we could jump to state 7 with the
               appropriate check here but I won\'t :) */
            switch (c)
            {
            case \'M\': state = 1; break;
            default: goto error_state;
            }
            break;
        case 1:     /* M */
            switch (c)
            {
            case \'y\': state = 2; break;
            default: goto error_state;
            }
            break;
        case 2:     /* My */
            switch (c)
            {
            case \'E\': state = 3; break;
            default: goto error_state;
            }
            break;
        case 3:     /* MyE */
            switch (c)
            {
            case \'n\': state = 4; break;
            default: goto error_state;
            }
            break;
        case 4:     /* MyEn */
            switch (c)
            {
            case \'u\': state = 5; break;
            default: goto error_state;
            }
            break;
        case 5:     /* MyEnu */
            switch (c)
            {
            case \'m\': state = 6; break;
            default: goto error_state;
            }
            break;
        case 6:     /* MyEnum */
            switch (c)
            {
            case \'_\': state = 7; break;
            default: goto error_state;
            }
            break;
        case 7:     /* MyEnum_ */
            switch (c)
            {
            case \'f\': state = 8; break;
            case \'b\': state = 11; break;
            default: goto error_state;
            }
            break;
        case 8:     /* MyEnum_f */
            switch (c)
            {
            case \'o\': state = 9; break;
            default: goto error_state;
            }
            break;
        case 9:     /* MyEnum_fo */
            switch (c)
            {
            case \'o\': state = 10; break;
            default: goto error_state;
            }
            break;
        case 10:    /* MyEnum_foo */
            switch (c)
            {
            case \'\\0\': result = MyEnum_foo; goto accept_state;
            default: goto error_state;
            }
            break;
        case 11:    /* MyEnum_b */
            switch (c)
            {
            case \'a\': state = 12; break;
            default: goto error_state;
            }
            break;
        case 12:    /* MyEnum_ba */
            switch (c)
            {
            case \'r\': state = 13; break;
            case \'z\': state = 14; break;
            default: goto error_state;
            }
            break;
        case 13:    /* MyEnum_bar */
            switch (c)
            {
            case \'\\0\': result = MyEnum_bar; goto accept_state;
            default: goto error_state;
            }
            break;
        case 14:    /* MyEnum_baz */
            switch (c)
            {
            case \'\\0\': result = MyEnum_baz; goto accept_state;
            default: goto error_state;
            }
            break;
        default:
            /* we shouldn\'t be here */
            assert(0);
        }
    }
error_state:
    /* handle error */
    result = (MyEnum)-1;
accept_state:
    return result;
}
    

要回复问题请先登录注册