在 C 中,我可以使用预处理器将enum
转换为其等效的字符串。
但将char*
转换为enum
是否有任何聪明的技巧。
我可以为每个字符串使用if语句和strcmp
并返回等效的enum
但是有更优雅的方法吗?
答案 0 :(得分:2)
请不要做这样的hackery。你有几乎肯定设计缺陷。
编辑:如果你真的必须出于某种原因这样做,我一起攻击了这个。该示例还应显示其用法:
#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;
}
答案 1 :(得分:2)
您可以使用enum和char *值创建一个结构,并为每次转换搜索它们。如果你有很多枚举类型,可能会派上用场。基于this thread。
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;
}
答案 2 :(得分:1)
我怀疑你真正想要的不是枚举,而是一种将整数映射到字符串并返回的方法。例如,您可能表示几个月,并使用整数来进行简单的比较(“这是该日期之前的日期”),以及允许更友好的表示的字符串。
(我不得不猜测,因为你的问题没有解释你实际上要解决的问题。)
如果放弃枚举,可以编写一个小助手库,以通用的方式解决这个问题。实现映射的一种非常简单的方法是使用简单的字符串数组,并使用数组中的索引作为整数:
#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。
答案 3 :(得分:0)
如果您可以信任char*
,并且它是否适合进行简单计算,请尝试以下方法:
#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 :(得分:0)
不是非常聪明但是如果你的字符串很少而且非常短(4个字节或适合整数类型的东西),你可以将它们重新整理为整数并用于开关等。 Hackery肯定,但有时可能会给你你想要的东西。
答案 5 :(得分:0)
没有任何“聪明”的方法来有效地 AFAIK。只需为您的特定枚举编写一个简单的解析器并完成它。当然有限制,这只适用于单个枚举,并且不适用于所有枚举。与其他语言一样,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;
}