Imprimer le texte au lieu de la valeur de C enum

87
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;
}
avi
la source
Votre sortie attendue est pour qu'il imprime la chaîne "dimanche", etc.?
GalacticCowboy

Réponses:

104

Les énumérations en C sont des nombres qui ont des noms pratiques dans votre code. Ce ne sont pas des chaînes et les noms qui leur sont attribués dans le code source ne sont pas compilés dans votre programme et ne sont donc pas accessibles lors de l'exécution.

La seule façon d'obtenir ce que vous voulez est d'écrire vous-même une fonction qui traduit la valeur d'énumération en une chaîne. Par exemple (en supposant ici que vous déplacez la déclaration de l' enum Daysextérieur de 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));

Vous pouvez également utiliser un tableau comme carte, par exemple

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

/* ... */

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

Mais ici, vous voudrez probablement assigner Sunday = 0l'énumération pour être sûr ... Je ne suis pas sûr si le standard C oblige les compilateurs à commencer les énumérations à partir de 0, bien que la plupart le fassent (je suis sûr que quelqu'un commentera pour confirmer ou refuser cela ).

Tyler McHenry
la source
3
Aw, vous m'avez battu à la solution de tableau. : P Mais oui, les énumérations commencent toujours à 0 sauf si vous spécifiez une valeur différente.
casablanca
1
Si je comptais sur l'utilisation des énumérations comme index, je préférerais en fait les numéroter explicitement. Inutile selon les normes, mais en tant que groupe, les compilateurs n'ont pas été exactement les meilleurs pour suivre les normes de mon expérience.
jdmichal
3
La norme C dit: "Si le premier énumérateur n'a pas =, la valeur de sa constante d'énumération est 0". Mais ça ne fait pas de mal de l'avoir explicite.
Michael Burr
17
N'oubliez pas qu'avec C99, vous pouvez le faire const char* dayNames[] = {[Sunday] = "Sunday", [Monday] = "Monday", [Tuesday] = "Tuesday", /* ... etc ... */ };. Vous savez, au cas où les jours de la semaine seraient réorganisés, ou si vous décidiez que le lundi est le premier jour de la semaine.
Tim Schaeffer
2
@ user3467349 Cela (stringification du préprocesseur) transforme simplement le symbole suivant le # en une chaîne. Donc, oui, #Monday deviendrait "Monday" mais Days TheDay = Monday; printf("%s", #TheDay);afficherait "TheDay".
Tyler McHenry
29

J'utilise quelque chose comme ça:

dans un fichier "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

puis dans n'importe quel fichier d'en-tête, vous faites la déclaration enum, 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)

puis dans un fichier appelé EnumToString.c:

#include "enum.h"

#define GENERATE_ENUM_STRINGS  // Start string generation

#include "enum.h"             

#undef GENERATE_ENUM_STRINGS   // Stop string generation

puis dans 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;
}

cela générera "automatiquement" les chaînes pour toutes les énumérations déclarées de cette façon et incluses dans "EnumToString.c"

Vargas
la source
4
C'est moche à lire, mais vous n'avez pas de duplication de données. (Contrairement à tout le monde.) Je suis déchiré sur l'opportunité d'aimer ça.
Kim Reece
1
+1 pour la solution incroyablement créative sans duplication de données et probablement la meilleure maintenabilité / flexibilité, mais yech! Je pense que je préfère toujours suivre la route const char * [].
manifeste du
4
Ouais, la maintenabilité est géniale! Il est vraiment facile de se mettre à jour lorsque les jours de la semaine changent! </sarcasm> D'ailleurs, ce n'est même pas utile à des fins de localisation puisque le mappage entre les chaînes anglaises et les noms dans le programme est maintenant codé en dur par votre tentative d'éviter la duplication. Au moins avec les autres approches, il est possible de traduire les chaînes sans changer chaque occurrence dans les fichiers source.
R .. GitHub STOP HELPING ICE
1
Vous pouvez probablement l'internationaliser en (avec quelque chose comme gettext) en changeant les instructions de retour en return _(#element)et autres.
Vargas
Lorsque le préprocesseur C est aussi utile mais aussi laid, je le remplace généralement par un simple générateur de code ou un préprocesseur personnalisé dans un langage de script. Et en fait, j'ai un script Python que j'ai utilisé exactement dans ce but dans plusieurs projets. Mais je ne l'utilise pas souvent de nos jours - pour de nombreux cas d'utilisation, vous pouvez vous en tirer simplement en utilisant des chaînes et sans vous soucier des enums (et encore plus en C ++ ou ObjC).
abarnert
6

La façon dont je fais généralement cela est de stocker les représentations de chaîne dans un tableau séparé dans le même ordre, puis d'indexer le tableau avec la valeur enum:

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

enumLes s en C ne fonctionnent pas vraiment comme vous l'attendez. Vous pouvez les considérer comme des constantes glorifiées (avec quelques avantages supplémentaires liés au fait d'être une collection de telles constantes), et le texte que vous avez écrit pour "Sunday" est vraiment résolu en un nombre lors de la compilation, le texte est finalement jeté.

En bref: pour faire ce que vous voulez vraiment, vous devrez conserver un tableau de chaînes ou créer une fonction pour mapper de la valeur de l'énumération au texte que vous souhaitez imprimer.

Mark Elliot
la source
4

Les énumérations en C sont essentiellement du sucre syntaxique pour les listes nommées de valeurs entières séquencées automatiquement. Autrement dit, lorsque vous avez ce code:

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

    Days TheDay = Monday;
}

Votre compilateur crache en fait ceci:

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

Par conséquent, la sortie d'une énumération C sous forme de chaîne n'est pas une opération qui a du sens pour le compilateur. Si vous souhaitez avoir des chaînes lisibles par l'homme pour celles-ci, vous devrez définir des fonctions à convertir des énumérations en chaînes.

jdmichal
la source
4

Voici une façon plus propre de le faire avec des 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;
}

Je ne suis pas sûr qu'il s'agisse de préprocesseurs n / b totalement portables, mais cela fonctionne avec gcc.

C'est c99 btw, donc utilisez-le c99 strictsi vous le branchez dans (le compilateur en ligne) ideone .

Tim Schaeffer
la source
Je dois adorer à quel point les macros «propres» sont :-).
mk12
3

Je sais que je suis en retard à la fête, mais qu'en est-il de ça?

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

De cette façon, vous n'avez pas à synchroniser manuellement le enumet la char*baie. Si vous êtes comme moi, il y a de fortes chances que vous changiez plus tard le enum, et le char*tableau affichera des chaînes non valides. Cela peut ne pas être une fonctionnalité universellement prise en charge. Mais afaik, la plupart des compilateurs C modernes prennent en charge ce style initialier désigné.

Vous pouvez en savoir plus sur les initialiseurs désignés ici .

quarante-deux
la source
1

La question est que vous voulez écrire le nom une seule fois.
J'ai un ider comme celui-ci:

#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";
    }

il utilise un struct pour insérer enum, de sorte qu'une imprimante à chaîne puisse suivre chaque valeur d'énumération définie.

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

La différence avec enum est que le constructeur ne peut pas signaler une erreur si les nombres sont répétés. si vous n'aimez pas écrire le numéro, vous __LINE__pouvez le remplacer:

#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

user8931844
la source
0

Je suis nouveau dans ce domaine mais une instruction switch fonctionnera sans aucun doute

#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;
}
Suraj K Thomas
la source
Un formatage correct (lire: indentation) doit être un must pour le code textuel dans les réponses ...
p4010
0

J'aime que cela ait enum dans le dayNames. Pour réduire la saisie, nous pouvons procéder comme suit:

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

const char* dayNames[] = { EP(Sunday), EP(Monday)};
Heng lou
la source
0

Il existe une autre solution: créez votre propre classe d'énumération dynamique. Cela signifie que vous avez une structfonction et une fonction pour créer une nouvelle énumération, qui stocke les éléments dans a structet que chaque élément a une chaîne pour le nom. Vous avez également besoin d'un certain type pour stocker des éléments individuels, des fonctions pour les comparer, etc. Voici un exemple:

#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 *parrent;
  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->parrent==b->parrent && 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;
}

Les fonctions d'énumération devraient être dans leur propre unité de traduction, mais je les ai combinées ici pour le rendre plus simple.

L'avantage est que cette solution est flexible, suit le principe DRY, vous pouvez stocker des informations avec chaque élément, vous pouvez créer de nouvelles énumérations pendant l'exécution et vous pouvez ajouter de nouveaux éléments pendant l'exécution. L'inconvénient est que cela est complexe, nécessite une allocation de mémoire dynamique, ne peut pas être utilisé dans switch- case, nécessite plus de mémoire et est plus lent. La question est de savoir si vous ne devez pas utiliser un langage de niveau supérieur dans les cas où vous en avez besoin.

12431234123412341234123
la source
-3

TheDay revient à un type entier. Donc:

printf("%s", TheDay);

Tente d'analyser TheDay sous forme de chaîne et affichera des déchets ou plantera.

printf n'est pas sécurisé et vous fait confiance pour lui transmettre la bonne valeur. Pour imprimer le nom de la valeur, vous devez créer une méthode pour mapper la valeur enum à une chaîne - soit une table de recherche, une instruction de commutateur géant, etc.

Michael
la source