【问题标题】:Mapping string to enum value将字符串映射到枚举值
【发布时间】:2025-12-03 13:35:01
【问题描述】:

在我的程序中,我有一个用于索引数组成员的枚举。原因是我更容易理解我正在访问的参数而不知道它在数组中的索引

enum param_enum
{
    AA,
    AB,
    AC,
    AD,
    PARAM_COUNT
};


static int16_t parameters[PARAM_COUNT] =
{
    [AA] = 5, 
    [AB] = 3, 
    [AC] = 4,
    [AD] = 8,
};

然后我可以访问任何参数,例如:

parameters[AA] = 10; // Update AA parameter to value 10.

我将收到串行命令,例如:

"AA:15"

当我收到这个命令时,我必须根据前2个字符确定我需要修改什么参数,然后跳过第3个字符(因为它只是“:”,我不关心它)剩下的字符会显示新值)

我想知道是否有更简单的方法将枚举映射到字符串

我目前的方法:

// line holds the string data
// cmd_size is the length of string data
bool parse_command(char *line, uint16_t cmd_size)
{
printf("data size = %u \n",cmd_size);
char temp_buf[3] = {0};
temp_buf[0] = line[0];
temp_buf[1] = line[1];
printf("temp_buf = %s \n",temp_buf);
if (!strcmp("aa", temp_buf))
    {
        printf("aa: detected \n");
        char temp_storage[5];
        int16_t final_value;
        for(int i = 3;i<=cmd_size; i++){
                temp_storage[i-3]=line[i]; // read data and append to temp bufferfrom the 3rd character till the end of line
                if(line[i] == 0){
                    printf("null termination triggered \n");
                    final_value = strtol(temp_storage,NULL,10); // convert char array to int16_t
                    printf("temp var = %i \n",final_value);             
                }
        }

        return true;
    }

}

上述方法似乎工作正常,但我认为这不是这个特定任务最合适的解决方案。

【问题讨论】:

  • (char*)param-&gt;write.value 是在 : 之后终止还是在它之后也有值? :之前的字符数是总是2还是变长?
  • “更容易”到底是什么意思?
  • 蓝牙命令字符串的末尾有一个空终止符。 :之后的数据并不总是长度为2
  • 对不起,我遗漏了一些重要信息。我已经更新了我最初的问题

标签: c string enums esp32 strcmp


【解决方案1】:

如果您不介意枚举常量的实际值是什么,您可以将这些值定义为等同于测试字符串的前两个字符。然后,您可以将前两个字符复制到该 enum 类型的变量中,然后该变量将直接采用适当的枚举。

您可以使用两个字符的整数文字定义值(例如'BA')。在 little-endian 系统(例如 Windows)上,这两个字符的顺序是相反的;对于大端系统,它们将按直接顺序排列。

这是一个小端实现示例:

#include <stdio.h>
#include <string.h>

enum param_enum {
    // Reverse byte order from strings for little-endian; keep "as-is" for big-endian...
    AA = 'AA',
    AB = 'BA',
    AC = 'CA',
    AD = 'DA'
};

int main(void)
{
    char test[100];
    while (1) {
        printf("Enter test string (Q to quit): ");
        if (scanf("%99s", test) != 1 || strcmp(test, "Q") == 0) break;
        enum param_enum penum;
        memset(&penum, 0, sizeof(penum));   // To clear any 'upper' bytes
        memcpy(&penum, test, 2);            // Now copy the first 2 byte2
        switch (penum) {
            case AA:
                printf("Code is AA.\n");
                break;
            case AB:
                printf("Code is AB.\n");
                break;
            case AC:
                printf("Code is AC.\n");
                break;
            case AD:
                printf("Code is AD.\n");
                break;
            default:
                printf("Unknown code.\n");
                break;
        }
    }
    return 0;
}

如果您的编译器不支持多字符文字(这种支持是可选的,根据 C 标准 IIRC),您可以使用十六进制常量和字符的 ASCII 代码指定等效值(假设您的平台使用 ASCII 编码),而是:

enum param_enum {
    AA = 0x4141, // 'AA'
    AB = 0x4241, // 'BA'
    AC = 0x4341, // 'CA'
    AD = 0x4441  // 'DA'
};

【讨论】:

  • 或者使用宏来构造值,例如'#define TWOCHAR(a, b) (((a) #define TWOCHAR(a, b) (((b) << 8) | (a)) 取决于字节序。理想情况下,代码应该避免字节序问题。
  • @IanAbbott 是的 - 宏很好。关于字节序 - 大概,OP 知道特定微控制器的含义,所以它不应该成为问题。 (如果要针对多个系统,可以使用定义一个或另一个的条件块。)
  • 在查看了您建议的这个示例后,我意识到它可能不适用于我的特定应用程序,因为我使用我的 ENUM 作为参数数组的索引,因此我无法将它们设置为字符串。我忘了在我最初的帖子中提到这个重要信息,但我现在已经更新了
  • || strcmp(test, "Q") == 0 可以是另一个 case
  • @chux 可能会生成类似:warning C4063: case '81' is not a valid value for switch of enum 'param_enum' (除非您添加 @987654328 @ 作为另一个枚举)。
【解决方案2】:

您可以使用X Macro 技术。

#define PARAM_XMACRO \
    X(AA) \
    X(AB) \
    X(AC) \
    X(AD)


enum param_enum{
#define X(NAME) NAME,
PARAM_XMACRO
#undef X
};

int process() {
    ...
    char *val = (char*)param->write.value;
#define X(NAME) \
    if (strcmp(val, #NAME ":") == 0) {                   \
            printf(#NAME " parameter need to change\n"); \
            return NAME;                                 \
    }
PARAM_XMACRO
#undef X
    return -1;
}

它将扩展为:(为清楚起见添加了换行符)

enum param_enum{
AA, AB, AC, AD,
};

int process() {
    ...
    char *val = (char*)param->write.value;

if (strcmp(val, "AA" ":") == 0) {
   printf("AA" " parameter need to change\n");
   return AA;
}
if (strcmp(val, "AB" ":") == 0) {
  printf("AB" " parameter need to change\n");
  return AB;
}
if (strcmp(val, "AC" ":") == 0) {
  printf("AC" " parameter need to change\n");
  return AC;
}
if (strcmp(val, "AD" ":") == 0) {
  printf("AD" " parameter need to change\n");
  return AD;
}
  return -1;
}

【讨论】:

    【解决方案3】:

    您可以使用由参数枚举索引的字符串查找表,以及从字符串中查找参数枚举的函数:

    enum param_enum {
        AA,
        AB,
        AC,
        AD
    };
    
    static const char * const param_prefix[] = {
        [AA] = "AA",
        [AB] = "AB",
        [AC] = "AC",
        [AD] = "AD",
    };
    
    #define ARRAYLEN(a) (sizeof (a) / sizeof (a)[0])
    
    int find_param(const char *value, size_t value_len) {
       int i;
       const char *colon = memchr(value, ':', value_len);
    
       if (!colon) {
           /* not found */
           return -1;
       }
    
       /* use length up to colon */
       value_len = colon - value;
    
       for (i = 0; i < ARRAYLEN(param_prefix); i++) {
           if (param_prefix[i]) {
               size_t prefix_len = strlen(param_prefix[i]);
    
               if (value_len == prefix_len &&
                   memcmp(param_prefix[i], value, prefix_len) == 0) {
                   /* found */
                   return i;
               }
           }
       }
       /* not found */
       return -1;
    }
    

    示例用法:

            // (using 3 for length here, but should use something better)
            int penum = find_param((char*)param->write.value, 3);
            if(penum >= 0) {
                printf("%s parameter need to change\n", param_prefix[penum]);
            }
    

    【讨论】:

    • 你好。我试图初始化以下数组:static const char * const param_prefix[] = { [SP] = "SP", [DF] = "DF", [SS] = "SS", [SD] = "SD", }; 我得到的错误是:sorry, unimplemented: non-trivial designated initializers not supported
    • 也许能得到一个至少实现 C99 的更好的编译器?无论如何,我刚刚修复了一个错误,我在几个地方使用了param_prefix 而不是param_prefix[i]
    • @TheBestPlayer 这很令人困惑,因为您在问题中使用了相同类型的指定初始化程序,除了映射到 int16_t 而不是 const char * 值。
    • 我使用 int16_t 因为每个参数值都是 int16_t 类型。我还在研究它。我将尝试构建一个非常简单的测试代码,它只包含枚举、我的参数数组和串行数据输入,这样我就可以输入一个文本字符串