Преобразование из String в Enum в C

Есть ли способ, который можно использовать для ввода строки (ввод пользователем) и преобразовать ее в значение Enumeration? В этом случае строка будет именем значения перечисления, например:

enum Day
{
    Sunday = 0,
    Monday = 1,
    ...
}

Таким образом, если пользователь дал имя дня, он сможет проанализировать его до соответствующего значения Enum.

Трюк заключается в том, что у меня более 500 значений, с которыми я работаю, и они распределены по нескольким перечислениям.

Я знаю метод Enum.Parse в С#, так есть какая-то форма этого в c?

Ответ 1

Стандартный способ его реализации - это что-то вроде:

typedef enum {value1, value2, value3, (...) } VALUE;

const static struct {
    VALUE      val;
    const char *str;
} conversion [] = {
    {value1, "value1"},
    {value2, "value2"},
    {value3, "value3"},
       (...)
};

VALUE
str2enum (const char *str)
{
     int j;
     for (j = 0;  j < sizeof (conversion) / sizeof (conversion[0]);  ++j)
         if (!strcmp (str, conversion[j].str))
             return conversion[j].val;    
     error_message ("no such string");
}

Обратное должно быть очевидно.

Ответ 2

Нет прямого пути, но с C вы импровизируете. Вот старый трюк. Пуристы могут отказаться от этого. Но это способ управлять такими вещами несколько здраво. Использует некоторые препроцессорные трюки.

В константах .h введите следующее:

CONSTANT(Sunday,  0)
CONSTANT(Monday,  1)
CONSTANT(Tuesday, 2)

В main.c:

#include <stdio.h>

#define CONSTANT(name, value) \
    name = value,

typedef enum {
    #include "constants.h"
} Constants;

#undef CONSTANT

#define CONSTANT(name, value) \
    #name,

char* constants[] = {
    #include "constants.h"
};  

Constants str2enum(char* name) {
    int ii;
    for (ii = 0; ii < sizeof(constants) / sizeof(constants[0]); ++ii) {
        if (!strcmp(name, constants[ii])) {
            return (Constants)ii;
        }   
    }   
    return (Constants)-1;
}   

int main() {
    printf("%s = %d\n", "Monday", str2enum("Monday"));
    printf("%s = %d\n", "Tuesday", str2enum("Tuesday"));
    return 0;
}

Вы можете попробовать другие варианты основной идеи.

Ответ 3

Предупреждение, это общий взлом. Вы можете использовать dlsym, чтобы выполнить поиск переменной, которая правильно инициализирована. Чтобы этот пример работал, вы должны скомпилировать, чтобы локальные символы могли быть видны динамическому компоновщику. В GCC опция -rdynamic.

enum Day {
    SunDay, MonDay, TuesDay, WednesDay, ThursDay, FriDay, SaturDay
};

enum Day Sunday = SunDay,
         Monday = MonDay,
         Tuesday = TuesDay,
         Wednesday = WednesDay,
         Thursday = ThursDay,
         Friday = FriDay,
         Saturday = SaturDay;

int main () {
    const char *daystr = "Thursday";
    void *h = dlopen(0, RTLD_NOW);
    enum Day *day = dlsym(h, daystr);
    if (day) printf("%s = %d\n", daystr, *day);
    else printf("%s not found\n", daystr);
    return 0;
}

Ответ 4

Если вы используете прямой C, нет эквивалента "Enum.Parse". Вы захотите написать свою собственную функцию, сравнив пользовательскую строку с предварительно определенными значениями с помощью strcmp(), а затем вернув соответствующее значение перечисления.

Другая возможность заключается в использовании существующей реализации "хэш-карты" или, например, сворачивании вашего собственного - например, в glib должно работать для вас: https://developer.gnome.org/glib/2.30/glib-Hash-Tables.html

Карта хеша должна быть быстрее, чем выполнять линейный поиск возможных значений enum, если их много (например, если вы делали что-то другое, кроме дней недели). Хорошая реализация хеш-карты должна быть близка к O (1) для поиска, а не O (n) для линейного поиска.

Ответ 5

Не совсем, но если вы используете хеш-функцию, вы можете настроить все значения вашего перечисления, чтобы соответствовать набору хешированных строк. Возможно, вам придется использовать более сложный хеш, если вас не волнует чувствительность к регистру.

Это, вероятно, ваше лучшее решение, поскольку оно имеет более низкие накладные расходы, чем strcmp (...). Назначение значения перечисления из хеша строки не требует повторных сопоставлений строк и т.д.

Ответ 6

Это было бы хорошим решением:

enum e_test { a, b, c, END };

enum e_test get_enum_value(char * val) {
    static char const * e_test_str[] = { "a", "b", "c" };
    for (int i = 0; i < END; ++i)
        if (!strcmp(e_test_str[i], val))
            return i;
    return END;
 }