简体   繁体   English

从 C 枚举打印文本而不是值

[英]Print text instead of value from C enum

int main()
{

  enum Days{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};

  Days TheDay;

  int j = 0;

  printf("Please enter the day of the week (0 to 6)\n");

  scanf("%d",&j);

  TheDay = Days(j);

  //how to PRINT THE VALUES stored in TheDay

  printf("%s",TheDay);  //   isnt working

  return 0;
}

Enumerations in C are numbers that have convenient names inside your code. C 中的枚举是在代码中具有方便名称的数字。 They are not strings, and the names assigned to them in the source code are not compiled into your program, and so they are not accessible at runtime.它们不是字符串,源代码中分配给它们的名称不会编译到您的程序中,因此在运行时无法访问它们。

The only way to get what you want is to write a function yourself that translates the enumeration value into a string.获得所需内容的唯一方法是自己编写一个函数,将枚举值转换为字符串。 Eg (assuming here that you move the declaration of enum Days outside of main ):例如(假设您将enum Days的声明移到main之外):

const char* getDayName(enum Days day) 
{
   switch (day) 
   {
      case Sunday: return "Sunday";
      case Monday: return "Monday";
      /* etc... */
   }
}

/* Then, later in main: */
printf("%s", getDayName(TheDay));

Alternatively, you could use an array as a map, eg或者,您可以使用数组作为地图,例如

const char* dayNames[] = {"Sunday", "Monday", "Tuesday", /* ... etc ... */ };

/* ... */

printf("%s", dayNames[TheDay]);

But here you would probably want to assign Sunday = 0 in the enumeration to be safe... I'm not sure if the C standard requires compilers to begin enumerations from 0, although most do (I'm sure someone will comment to confirm or deny this).但是在这里您可能希望在枚举中分配Sunday = 0以确保安全...我不确定 C 标准是否要求编译器从 0 开始枚举,尽管大多数都这样做(我确定有人会评论以确认或否认这一点)。

I use something like this:我使用这样的东西:

in a file "EnumToString.h":在文件“EnumToString.h”中:

#undef DECL_ENUM_ELEMENT
#undef DECL_ENUM_ELEMENT_VAL
#undef DECL_ENUM_ELEMENT_STR
#undef DECL_ENUM_ELEMENT_VAL_STR
#undef BEGIN_ENUM
#undef END_ENUM

#ifndef GENERATE_ENUM_STRINGS
    #define DECL_ENUM_ELEMENT( element ) element,
    #define DECL_ENUM_ELEMENT_VAL( element, value ) element = value,
    #define DECL_ENUM_ELEMENT_STR( element, descr ) DECL_ENUM_ELEMENT( element )
    #define DECL_ENUM_ELEMENT_VAL_STR( element, value, descr ) DECL_ENUM_ELEMENT_VAL( element, value )
    #define BEGIN_ENUM( ENUM_NAME ) typedef enum tag##ENUM_NAME
    #define END_ENUM( ENUM_NAME ) ENUM_NAME; \
            const char* GetString##ENUM_NAME(enum tag##ENUM_NAME index);
#else
    #define BEGIN_ENUM( ENUM_NAME) const char * GetString##ENUM_NAME( enum tag##ENUM_NAME index ) {\
        switch( index ) { 
    #define DECL_ENUM_ELEMENT( element ) case element: return #element; break;
    #define DECL_ENUM_ELEMENT_VAL( element, value ) DECL_ENUM_ELEMENT( element )
    #define DECL_ENUM_ELEMENT_STR( element, descr ) case element: return descr; break;
    #define DECL_ENUM_ELEMENT_VAL_STR( element, value, descr ) DECL_ENUM_ELEMENT_STR( element, descr )

    #define END_ENUM( ENUM_NAME ) default: return "Unknown value"; } } ;

#endif

then in any header file you make the enum declaration, day enum.h然后在任何头文件中进行枚举声明,day enum.h

#include "EnumToString.h"

BEGIN_ENUM(Days)
{
    DECL_ENUM_ELEMENT(Sunday) //will render "Sunday"
    DECL_ENUM_ELEMENT(Monday) //will render "Monday"
    DECL_ENUM_ELEMENT_STR(Tuesday, "Tuesday string") //will render "Tuesday string"
    DECL_ENUM_ELEMENT(Wednesday) //will render "Wednesday"
    DECL_ENUM_ELEMENT_VAL_STR(Thursday, 500, "Thursday string") // will render "Thursday string" and the enum will have 500 as value
    /* ... and so on */
}
END_ENUM(MyEnum)

then in a file called EnumToString.c:然后在一个名为 EnumToString.c 的文件中:

#include "enum.h"

#define GENERATE_ENUM_STRINGS  // Start string generation

#include "enum.h"             

#undef GENERATE_ENUM_STRINGS   // Stop string generation

then in main.c:然后在 main.c 中:

int main(int argc, char* argv[])
{
    Days TheDay = Monday;
    printf( "%d - %s\n", TheDay, GetStringDay(TheDay) ); //will print "1 - Monday"

    TheDay = Thursday;
    printf( "%d - %s\n", TheDay, GetStringDay(TheDay) ); //will print "500 - Thursday string"

    return 0;
}

this will generate "automatically" the strings for any enums declared this way and included in "EnumToString.c"这将为任何以这种方式声明并包含在“EnumToString.c”中的枚举“自动”生成字符串

The way I usually do this is by storing the string representations in a separate array in the same order, then indexing the array with the enum value:我通常这样做的方法是将字符串表示形式以相同的顺序存储在一个单独的数组中,然后使用枚举值索引数组:

const char *DayNames[] = { "Sunday", "Monday", "Tuesday", /* etc */ };
printf("%s", DayNames[Sunday]); // prints "Sunday"

enum s in C don't really work the way you're expecting them to. C 中的enum并没有真正按照您期望的方式工作。 You can think of them kind of like glorified constants (with a few additional benefits relating to being a collection of such constants), and the text you've written in for "Sunday" really gets resolved to a number during compilation, the text is ultimately discarded.你可以把它们想象成美化的常量(作为这些常量的集合还有一些额外的好处),并且你为“星期日”编写的文本在编译过程中真的被解析为一个数字,文本是最终被丢弃。

In short: to do what you really want you'll need to keep an array of the strings or create a function to map from the enum's value to the text you'd like to print.简而言之:要执行您真正想要的操作,您需要保留一个字符串数组或创建一个函数以将枚举值映射到您想要打印的文本。

Enumerations in C are basically syntactical sugar for named lists of automatically-sequenced integer values. C 中的枚举基本上是自动排序的整数值命名列表的语法糖。 That is, when you have this code:也就是说,当你有这个代码时:

int main()
{
    enum Days{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};

    Days TheDay = Monday;
}

Your compiler actually spits out this:你的编译器实际上吐出了这个:

int main()
{
    int TheDay = 1; // Monday is the second enumeration, hence 1. Sunday would be 0.
}

Therefore, outputting a C enumeration as a string is not an operation that makes sense to the compiler.因此,将 C 枚举作为字符串输出对于编译器来说不是有意义的操作。 If you want to have human-readable strings for these, you will need to define functions to convert from enumerations to strings.如果您想为这些提供人类可读的字符串,则需要定义函数以将枚举转换为字符串。

Here's a cleaner way to do it with macros:这是使用宏实现的更简洁的方法:

#include <stdio.h>
#include <stdlib.h>

#define DOW(X, S)                                                         \
    X(Sunday) S X(Monday) S X(Tuesday) S X(Wednesday) S X(Thursday) S X(Friday) S X(Saturday)

#define COMMA ,

/* declare the enum */
#define DOW_ENUM(DOW) DOW
enum dow {
    DOW(DOW_ENUM, COMMA)
};

/* create an array of strings with the enum names... */
#define DOW_ARR(DOW ) [DOW] = #DOW
const char * const dow_str[] = {
    DOW(DOW_ARR, COMMA)
};

/* ...or create a switchy function. */
static const char * dowstr(int i)
{
#define DOW_CASE(D) case D: return #D

    switch(i) {
        DOW(DOW_CASE, ;);
    default: return NULL;
    }
}


int main(void)
{
    for(int i = 0; i < 7; i++)
        printf("[%d] = «%s»\n", i, dow_str[i]);
    printf("\n");
    for(int i = 0; i < 7; i++)
        printf("[%d] = «%s»\n", i, dowstr(i));
    return 0;
}

I'm not sure that this is totally portable b/w preprocessors, but it works with gcc.我不确定这是否是完全可移植的黑白预处理器,但它适用于 gcc。

This is c99 btw, so use c99 strict if you plug it into (the online compiler) ideone .这是 c99 顺便说一句,所以如果你把它插入(在线编译器)ideone ,请使用c99 strict

I know I am late to the party, but how about this?我知道我参加聚会迟到了,但是这个怎么样?

const char* dayNames[] = { [Sunday] = "Sunday", [Monday] = "Monday", /*and so on*/ };
printf("%s", dayNames[Sunday]); // prints "Sunday"

This way, you do not have to manually keep the enum and the char* array in sync.这样,您不必手动保持enumchar*数组同步。 If you are like me, chances are that you will later change the enum , and the char* array will print invalid strings.如果您像我一样,以后可能会更改enum ,而char*数组将打印无效字符串。 This may not be a feature universally supported.这可能不是普遍支持的功能。 But afaik, most of the mordern day C compilers support this designated initialier style.但是 afaik,大多数现代 C 编译器都支持这种指定的初始化样式。

You can read more about designated initializers here .您可以在此处阅读有关指定初始值设定项的更多信息。

I like this to have enum in the dayNames.我喜欢在 dayNames 中有枚举。 To reduce typing, we can do the following:为了减少打字,我们可以执行以下操作:

#define EP(x) [x] = #x  /* ENUM PRINT */

const char* dayNames[] = { EP(Sunday), EP(Monday)};

The question is you want write the name just one times.问题是你只想写一次名字。
I have an ider like this:我有一个像这样的 ider:

#define __ENUM(situation,num) \
    int situation = num;        const char * __##situation##_name = #situation;

    const struct {
        __ENUM(get_other_string, -203);//using a __ENUM Mirco make it ease to write, 
        __ENUM(get_negative_to_unsigned, -204);
        __ENUM(overflow,-205);
//The following two line showing the expanding for __ENUM
        int get_no_num = -201;      const char * __get_no_num_name = "get_no_num";
        int get_float_to_int = -202;        const char * get_float_to_int_name = "float_to_int_name";

    }eRevJson;
#undef __ENUM
    struct sIntCharPtr { int value; const char * p_name; };
//This function transform it to string.
    inline const char * enumRevJsonGetString(int num) {
        sIntCharPtr * ptr = (sIntCharPtr *)(&eRevJson);
        for (int i = 0;i < sizeof(eRevJson) / sizeof(sIntCharPtr);i++) {
            if (ptr[i].value == num) {
                return ptr[i].p_name;
            }
        }
        return "bad_enum_value";
    }

it uses a struct to insert enum, so that a printer to string could follows each enum value define.它使用一个结构来插入枚举,以便打印机到字符串可以跟随每个枚举值定义。

int main(int argc, char *argv[]) {  
    int enum_test = eRevJson.get_other_string;
    printf("error is %s, number is %d\n", enumRevJsonGetString(enum_test), enum_test);

>error is get_other_string, number is -203

The difference to enum is builder can not report error if the numbers are repeated.与 enum 的区别在于,如果数字重复,builder 不会报告错误。 if you don't like write number, __LINE__ could replace it:如果你不喜欢写数字, __LINE__可以替换它:

#define ____LINE__ __LINE__
#define __ENUM(situation) \
    int situation = (____LINE__ - __BASELINE -2);       const char * __##situation##_name = #situation;
constexpr int __BASELINE = __LINE__;
constexpr struct {
    __ENUM(Sunday);
    __ENUM(Monday);
    __ENUM(Tuesday);
    __ENUM(Wednesday);
    __ENUM(Thursday);
    __ENUM(Friday);
    __ENUM(Saturday);
}eDays;
#undef __ENUM
inline const char * enumDaysGetString(int num) {
    sIntCharPtr * ptr = (sIntCharPtr *)(&eDays);
    for (int i = 0;i < sizeof(eDays) / sizeof(sIntCharPtr);i++) {
        if (ptr[i].value == num) {
            return ptr[i].p_name;
        }
    }
    return "bad_enum_value";
}
int main(int argc, char *argv[]) {  
    int d = eDays.Wednesday;
    printf("day %s, number is %d\n", enumDaysGetString(d), d);
    d = 1;
    printf("day %s, number is %d\n", enumDaysGetString(d), d);
}

>day Wednesday, number is 3 >day Monday, number is 1 >day Wednesday, number is 3 >day Monday, number is 1

i'm new to this but a switch statement will defenitely work我是新手,但 switch 语句肯定会起作用

#include <stdio.h>

enum mycolor;

int main(int argc, const char * argv[])

{
enum Days{Sunday=1,Monday=2,Tuesday=3,Wednesday=4,Thursday=5,Friday=6,Saturday=7};

enum Days TheDay;


printf("Please enter the day of the week (0 to 6)\n");

scanf("%d",&TheDay);

switch (TheDay)
 {

case Sunday:
        printf("the selected day is sunday");
        break;
    case Monday:
        printf("the selected day is monday");
        break;
    case Tuesday:
        printf("the selected day is Tuesday");
        break;
    case Wednesday:
        printf("the selected day is Wednesday");
        break;
    case Thursday:
        printf("the selected day is thursday");
        break;
    case Friday:
        printf("the selected day is friday");
        break;
    case Saturday:
        printf("the selected day is Saturaday");
        break;
    default:
        break;
}

return 0;
}

There is another solution: Create your own dynamic enumeration class.还有另一种解决方案:创建自己的动态枚举类。 Means you have a struct and some function to create a new enumeration, which stores the elements in a struct and each element has a string for the name.意味着你有一个struct和一些函数来创建一个新的枚举,它将元素存储在一个struct ,每个元素都有一个名称字符串。 You also need some type to store a individual elements, functions to compare them and so on.您还需要某种类型来存储单个元素、比较它们的函数等等。 Here is an example:下面是一个例子:

#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


struct Enumeration_element_T
{
  size_t index;
  struct Enumeration_T *parent;
  char *name;
};

struct Enumeration_T
{
  size_t len;
  struct Enumeration_element_T elements[];
};
  


void enumeration_delete(struct Enumeration_T *self)
{
  if(self)
  {
    while(self->len--)
    {
      free(self->elements[self->len].name);
    }
    free(self);
  }
}

struct Enumeration_T *enumeration_create(size_t len,...)
{
  //We do not check for size_t overflows, but we should.
  struct Enumeration_T *self=malloc(sizeof(self)+sizeof(self->elements[0])*len);
  if(!self)
  {
    return NULL;
  }
  self->len=0;
  va_list l; 
  va_start(l,len);
  for(size_t i=0;i<len;i++)
  {
    const char *name=va_arg(l,const char *);
    self->elements[i].name=malloc(strlen(name)+1);
    if(!self->elements[i].name)
    {
      enumeration_delete(self);
      return NULL;
    }
    strcpy(self->elements[i].name,name);
    self->len++;
  }
  return self;
}


bool enumeration_isEqual(struct Enumeration_element_T *a,struct Enumeration_element_T *b)
{
  return a->parent==b->parent && a->index==b->index;
}

bool enumeration_isName(struct Enumeration_element_T *a, const char *name)
{
  return !strcmp(a->name,name);
}

const char *enumeration_getName(struct Enumeration_element_T *a)
{
  return a->name;
}

struct Enumeration_element_T *enumeration_getFromName(struct Enumeration_T *self, const char *name)
{
  for(size_t i=0;i<self->len;i++)
  {
    if(enumeration_isName(&self->elements[i],name))
    {
      return &self->elements[i];
    }
  }
  return NULL;
}
  
struct Enumeration_element_T *enumeration_get(struct Enumeration_T *self, size_t index)
{
  return &self->elements[index];
}

size_t enumeration_getCount(struct Enumeration_T *self)
{
  return self->len;
}

bool enumeration_isInRange(struct Enumeration_T *self, size_t index)
{
  return index<self->len;
}



int main(void)
{
  struct Enumeration_T *weekdays=enumeration_create(7,"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday");
  if(!weekdays)
  {
    return 1;
  }
    
  printf("Please enter the day of the week (0 to 6)\n");
  size_t j = 0;
  if(scanf("%zu",&j)!=1)
  {
    enumeration_delete(weekdays);
    return 1;
  }
  // j=j%enumeration_getCount(weekdays); //alternative way to make sure j is in range
  if(!enumeration_isInRange(weekdays,j))
  {
    enumeration_delete(weekdays);
    return 1;
  }

  struct Enumeration_element_T *day=enumeration_get(weekdays,j);
  

  printf("%s\n",enumeration_getName(day));
  
  enumeration_delete(weekdays);

  return 0;
}

The functions of enumeration should be in their own translation unit, but i combined them here to make it simpler.枚举的功能应该在它们自己的翻译单元中,但我在这里将它们组合起来以使其更简单。

The advantage is that this solution is flexible, follows the DRY principle, you can store information along with each element, you can create new enumerations during runtime and you can add new elements during runtime.优点是该解决方案灵活,遵循 DRY 原则,您可以将信息与每个元素一起存储,您可以在运行时创建新的枚举,并且可以在运行时添加新元素。 The disadvantage is that this is complex, needs dynamic memory allocation, can't be used in switch - case , needs more memory and is slower.缺点是比较复杂,需要动态内存分配,不能用于switch - case ,需要更多内存,速度较慢。 The question is if you should not use a higher level language in cases where you need this.问题是在需要的情况下是否不应该使用高级语言。

Using a Macro and stringize operator(#) we can achieve this....使用宏和字符串化运算符(#)我们可以实现这一点....

#include <stdio.h>
typedef enum
{
    MON=0,
    TUE
}week;
int main()
{
    #define printt(data)        printf("%s",#data);
    printt(MON);
    return 0;
}

TheDay maps back to some integer type. TheDay 映射回某种整数类型。 So:所以:

printf("%s", TheDay);

Attempts to parse TheDay as a string, and will either print out garbage or crash.尝试将 TheDay 解析为字符串,并且将打印出垃圾或崩溃。

printf is not typesafe and trusts you to pass the right value to it. printf 不是类型安全的,并且相信您将正确的值传递给它。 To print out the name of the value, you'd need to create some method for mapping the enum value to a string - either a lookup table, giant switch statement, etc.要打印出值的名称,您需要创建一些将枚举值映射到字符串的方法 - 查找表、巨型 switch 语句等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM