So konvertieren Sie Aufzählungsnamen in Zeichenfolgen in c

Lesezeit: 10 Minuten

Mishas Benutzeravatar
Mischa

Gibt es eine Möglichkeit, Enumeratornamen in C in Strings umzuwandeln?

Benutzeravatar von Terrence M
Terence M

Eine Möglichkeit, den Präprozessor die Arbeit erledigen zu lassen. Es stellt auch sicher, dass Ihre Enums und Strings synchron sind.

#define FOREACH_FRUIT(FRUIT) \
        FRUIT(apple)   \
        FRUIT(orange)  \
        FRUIT(grape)   \
        FRUIT(banana)  \

#define GENERATE_ENUM(ENUM) ENUM,
#define GENERATE_STRING(STRING) #STRING,

enum FRUIT_ENUM {
    FOREACH_FRUIT(GENERATE_ENUM)
};

static const char *FRUIT_STRING[] = {
    FOREACH_FRUIT(GENERATE_STRING)
};

Nachdem der Präprozessor fertig ist, haben Sie:

enum FRUIT_ENUM {
    apple, orange, grape, banana,
};

static const char *FRUIT_STRING[] = {
    "apple", "orange", "grape", "banana",
};

Dann könntest du so etwas machen:

printf("enum apple as a string: %s\n",FRUIT_STRING[apple]);

Wenn der Anwendungsfall buchstäblich nur den Aufzählungsnamen ausgibt, fügen Sie die folgenden Makros hinzu:

#define str(x) #x
#define xstr(x) str(x)

Dann mach:

printf("enum apple as a string: %s\n", xstr(apple));

In diesem Fall scheint das zweistufige Makro überflüssig zu sein, aber aufgrund der Funktionsweise der Stringifizierung in C ist es in einigen Fällen notwendig. Nehmen wir zum Beispiel an, wir möchten ein #define mit einer Aufzählung verwenden:

#define foo apple

int main() {
    printf("%s\n", str(foo));
    printf("%s\n", xstr(foo));
}

Die Ausgabe wäre:

foo
apple

Dies liegt daran, dass str die Eingabe foo stringisiert, anstatt sie zu apple zu erweitern. Durch die Verwendung von xstr wird zuerst die Makroerweiterung durchgeführt, dann wird das Ergebnis gestringt.

Sehen Stringifizierung für mehr Informationen.

  • Ganz einfach wie du es beschrieben hast. Ich möchte nur enum to string und string to enum für einen Enum-Typ in einer Header-Datei. Ich möchte keine Implementierungsdatei (für C++/Objective-C) erstellen, wenn ich mich mit Enumerationen befasse.

    – p0lAris

    29. April 2015 um 14:53 Uhr

  • Sie könnten das FRUIT_STRING-Array iterieren und einen Zeichenfolgenvergleich durchführen. Wenn eine Übereinstimmung gefunden wird, dann ist der Index der Wert der ENUM, unter der Annahme von Nicht-Sparse-Enumerationen.

    – Terence M

    29. April 2015 um 15:37 Uhr

  • Wenn Sie den Namensraum nicht mit apple und orange… verschmutzen wollen, können Sie ihm voranstellen #define GENERATE_ENUM(ENUM) PREFIX##ENUM,

    – jsaak

    5. Juli 2016 um 18:26 Uhr

  • Dieser Ansatz hat den Nachteil, dass es nicht einfach ist, herauszufinden, wo ein Aufzählungselement definiert wurde.

    – Brita_

    12. Juli 2016 um 12:48 Uhr

  • Für diejenigen, die auf diesen Beitrag stoßen, wird diese Methode der Verwendung einer Makroliste zum Aufzählen verschiedener Elemente in einem Programm informell als “X-Makros” bezeichnet.

    – Ludin

    3. September 2018 um 6:59 Uhr


Benutzeravatar von Richard J. Ross III
Richard J. Ross III

In einer Situation, in der Sie Folgendes haben:

enum fruit {
    apple, 
    orange, 
    grape,
    banana,
    // etc.
};

Ich füge dies gerne in die Header-Datei ein, in der die Aufzählung definiert ist:

static inline char *stringFromFruit(enum fruit f)
{
    static const char *strings[] = { "apple", "orange", "grape", "banana", /* continue for rest of values */ };

    return strings[f];
}

  • Für das Leben von mir kann ich nicht sehen, wie das hilft. Könnten Sie es etwas erweitern, um es deutlicher zu machen.

    – David Heffernan

    28. März 2012 um 12:22 Uhr

  • Okay, wie hilft das? Wollen Sie sagen, es ist einfacher zu tippen enumToString(apple) als zu tippen "apple"? Es ist nicht so, dass es irgendwo Typsicherheit gibt. Wenn mir nicht etwas fehlt, ist das, was Sie hier vorschlagen, sinnlos und es gelingt nur, Code zu verschleiern.

    – David Heffernan

    28. März 2012 um 12:32 Uhr


  • Okay, jetzt verstehe ich. Das Makro ist meiner Ansicht nach falsch und ich schlage vor, Sie löschen es.

    – David Heffernan

    28. März 2012 um 12:41 Uhr

  • Kommentare sprechen über Makro. Wo ist es?

    – Sandeep

    4. April 2014 um 7:01 Uhr

  • Dies ist auch unbequem in der Wartung. Wenn ich eine neue Aufzählung einfüge, muss ich daran denken, diese auch im Array an der richtigen Position zu duplizieren.

    – Fabio

    22. Juni 2017 um 7:01 Uhr

Benutzeravatar von Maschina
Maschina

Ich habe einen C-Präprozessor-Trick gefunden, der die gleiche Aufgabe erfüllt ohne Deklarieren eines dedizierten Array-Strings (Quelle: http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/c_preprocessor_applications_en).

Sequentielle Aufzählungen

Nach der Erfindung von Stefan Ram können sequentielle Aufzählungen (ohne explizite Indexangabe, z enum {foo=-1, foo1 = 1}) kann wie dieser geniale Trick realisiert werden:

#include <stdio.h>

#define NAMES C(RED)C(GREEN)C(BLUE)
#define C(x) x,
enum color { NAMES TOP };
#undef C

#define C(x) #x,    
const char * const color_name[] = { NAMES };

Dies ergibt folgendes Ergebnis:

int main( void )  { 
    printf( "The color is %s.\n", color_name[ RED ]);  
    printf( "There are %d colors.\n", TOP ); 
}

Die Farbe ist ROT.
Es gibt 3 Farben.

Nicht sequentielle Aufzählungen

Da ich Fehlercodedefinitionen einem Array-String zuordnen wollte, damit ich die rohe Fehlerdefinition an den Fehlercode anhängen kann (z "The error is 3 (LC_FT_DEVICE_NOT_OPENED)."), habe ich den Code so erweitert, dass man den benötigten Index für die jeweiligen Enum-Werte einfach ermitteln kann:

#define LOOPN(n,a) LOOP##n(a)
#define LOOPF ,
#define LOOP2(a) a LOOPF a LOOPF
#define LOOP3(a) a LOOPF a LOOPF a LOOPF
#define LOOP4(a) a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP5(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP6(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP7(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP8(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP9(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF


#define LC_ERRORS_NAMES \
    Cn(LC_RESPONSE_PLUGIN_OK, -10) \
    Cw(8) \
    Cn(LC_RESPONSE_GENERIC_ERROR, -1) \
    Cn(LC_FT_OK, 0) \
    Ci(LC_FT_INVALID_HANDLE) \
    Ci(LC_FT_DEVICE_NOT_FOUND) \
    Ci(LC_FT_DEVICE_NOT_OPENED) \
    Ci(LC_FT_IO_ERROR) \
    Ci(LC_FT_INSUFFICIENT_RESOURCES) \
    Ci(LC_FT_INVALID_PARAMETER) \
    Ci(LC_FT_INVALID_BAUD_RATE) \
    Ci(LC_FT_DEVICE_NOT_OPENED_FOR_ERASE) \
    Ci(LC_FT_DEVICE_NOT_OPENED_FOR_WRITE) \
    Ci(LC_FT_FAILED_TO_WRITE_DEVICE) \
    Ci(LC_FT_EEPROM_READ_FAILED) \
    Ci(LC_FT_EEPROM_WRITE_FAILED) \
    Ci(LC_FT_EEPROM_ERASE_FAILED) \
    Ci(LC_FT_EEPROM_NOT_PRESENT) \
    Ci(LC_FT_EEPROM_NOT_PROGRAMMED) \
    Ci(LC_FT_INVALID_ARGS) \
    Ci(LC_FT_NOT_SUPPORTED) \
    Ci(LC_FT_OTHER_ERROR) \
    Ci(LC_FT_DEVICE_LIST_NOT_READY)


#define Cn(x,y) x=y,
#define Ci(x) x,
#define Cw(x)
enum LC_errors { LC_ERRORS_NAMES TOP };
#undef Cn
#undef Ci
#undef Cw
#define Cn(x,y) #x,
#define Ci(x) #x,
#define Cw(x) LOOPN(x,"")
static const char* __LC_errors__strings[] = { LC_ERRORS_NAMES };
static const char** LC_errors__strings = &__LC_errors__strings[10];

In diesem Beispiel Der C-Präprozessor generiert den folgenden Code:

enum LC_errors { LC_RESPONSE_PLUGIN_OK=-10,  LC_RESPONSE_GENERIC_ERROR=-1, LC_FT_OK=0, LC_FT_INVALID_HANDLE, LC_FT_DEVICE_NOT_FOUND, LC_FT_DEVICE_NOT_OPENED, LC_FT_IO_ERROR, LC_FT_INSUFFICIENT_RESOURCES, LC_FT_INVALID_PARAMETER, LC_FT_INVALID_BAUD_RATE, LC_FT_DEVICE_NOT_OPENED_FOR_ERASE, LC_FT_DEVICE_NOT_OPENED_FOR_WRITE, LC_FT_FAILED_TO_WRITE_DEVICE, LC_FT_EEPROM_READ_FAILED, LC_FT_EEPROM_WRITE_FAILED, LC_FT_EEPROM_ERASE_FAILED, LC_FT_EEPROM_NOT_PRESENT, LC_FT_EEPROM_NOT_PROGRAMMED, LC_FT_INVALID_ARGS, LC_FT_NOT_SUPPORTED, LC_FT_OTHER_ERROR, LC_FT_DEVICE_LIST_NOT_READY, TOP };

static const char* __LC_errors__strings[] = { "LC_RESPONSE_PLUGIN_OK", "" , "" , "" , "" , "" , "" , "" , "" "LC_RESPONSE_GENERIC_ERROR", "LC_FT_OK", "LC_FT_INVALID_HANDLE", "LC_FT_DEVICE_NOT_FOUND", "LC_FT_DEVICE_NOT_OPENED", "LC_FT_IO_ERROR", "LC_FT_INSUFFICIENT_RESOURCES", "LC_FT_INVALID_PARAMETER", "LC_FT_INVALID_BAUD_RATE", "LC_FT_DEVICE_NOT_OPENED_FOR_ERASE", "LC_FT_DEVICE_NOT_OPENED_FOR_WRITE", "LC_FT_FAILED_TO_WRITE_DEVICE", "LC_FT_EEPROM_READ_FAILED", "LC_FT_EEPROM_WRITE_FAILED", "LC_FT_EEPROM_ERASE_FAILED", "LC_FT_EEPROM_NOT_PRESENT", "LC_FT_EEPROM_NOT_PROGRAMMED", "LC_FT_INVALID_ARGS", "LC_FT_NOT_SUPPORTED", "LC_FT_OTHER_ERROR", "LC_FT_DEVICE_LIST_NOT_READY", };

Daraus ergeben sich folgende Umsetzungsmöglichkeiten:

LC_errors__strings[-1]
==> LC_Fehler__Strings[LC_RESPONSE_GENERIC_ERROR]
==> “LC_RESPONSE_GENERIC_ERROR”

  • Nett. Genau das habe ich gesucht und verwendet. Gleiche Fehler 🙂

    – MR Bean

    20. März 2020 um 2:19 Uhr

  • LOOP3…LOOP9 können jeweils in Form von LOOP2 – LOOP8 definiert werden.

    – Michael

    28. Juni 2021 um 23:08 Uhr

  • @Michael: Guter Punkt. Auch wenn es schwieriger zu warten ist, produziert es kürzeren Code 😉

    – Maschina

    5. Juli 2021 um 10:23 Uhr

  • Perfekt. Vergiss nicht zu definieren color_name als statisch, wenn es sich in einer Header-Datei befindet.

    – DimP

    6. September um 13:42 Uhr

Benutzeravatar von jyvet
jyvet

Sie müssen sich nicht auf den Präprozessor verlassen, um sicherzustellen, dass Ihre Aufzählungen und Zeichenfolgen synchron sind. Für mich macht die Verwendung von Makros den Code schwerer lesbar.

Verwenden von Enum und einem Array von Zeichenfolgen

enum fruit                                                                   
{
    APPLE = 0, 
    ORANGE, 
    GRAPE,
    BANANA,
    /* etc. */
    FRUIT_MAX                                                                                                                
};   

const char * const fruit_str[] =
{
    [BANANA] = "banana",
    [ORANGE] = "orange",
    [GRAPE]  = "grape",
    [APPLE]  = "apple",
    /* etc. */  
};

Hinweis: Die Saiten in der fruit_str array müssen nicht in derselben Reihenfolge deklariert werden wie die Enum-Elemente.

So verwenden Sie es

printf("enum apple as a string: %s\n", fruit_str[APPLE]);

Hinzufügen einer Überprüfung der Kompilierzeit

Wenn Sie Angst haben, eine Zeichenfolge zu vergessen, können Sie die folgende Überprüfung hinzufügen:

#define ASSERT_ENUM_TO_STR(sarray, max) \                                       
  typedef char assert_sizeof_##max[(sizeof(sarray)/sizeof(sarray[0]) == (max)) ? 1 : -1]

ASSERT_ENUM_TO_STR(fruit_str, FRUIT_MAX);

Beim Kompilieren wird ein Fehler gemeldet, wenn die Anzahl der Aufzählungselemente nicht mit der Anzahl der Zeichenfolgen im Array übereinstimmt.

Es gibt keinen einfachen Weg, dies direkt zu erreichen. Aber P99 verfügt über Makros, mit denen Sie solche Funktionen automatisch erstellen können:

 P99_DECLARE_ENUM(color, red, green, blue);

in einer Header-Datei und

 P99_DEFINE_ENUM(color);

in einer Kompilationseinheit (.c-Datei) sollte dann reichen, in diesem Beispiel würde die Funktion dann aufgerufen werden color_getname.

Benutzeravatar von Lars
Lars

Eine einfachere Alternative zu Hokyos “Non-Sequential Enums” -Antwort, basierend auf der Verwendung von Bezeichnern zum Instanziieren des String-Arrays:

#define NAMES C(RED, 10)C(GREEN, 20)C(BLUE, 30)
#define C(k, v) k = v,
enum color { NAMES };
#undef C

#define C(k, v) [v] = #k,    
const char * const color_name[] = { NAMES };

Eine solche Funktion ohne Validierung der Aufzählung ist ein wenig gefährlich. Ich schlage vor, eine switch-Anweisung zu verwenden. Ein weiterer Vorteil ist, dass dies für Aufzählungen mit definierten Werten verwendet werden kann, beispielsweise für Flags mit den Werten 1,2,4,8,16 usw.

Fassen Sie auch alle Ihre Enum-Strings in einem Array zusammen: –

static const char * allEnums[] = {
    "Undefined",
    "apple",
    "orange"
    /* etc */
};

Definieren Sie die Indizes in einer Header-Datei: –

#define ID_undefined       0
#define ID_fruit_apple     1
#define ID_fruit_orange    2
/* etc */

Dies erleichtert die Erstellung unterschiedlicher Versionen, wenn Sie beispielsweise internationale Versionen Ihres Programms mit anderen Sprachen erstellen möchten.

Verwenden eines Makros, auch in der Header-Datei: –

#define CASE(type,val) case val: index = ID_##type##_##val; break;

Erstellen Sie eine Funktion mit einer switch-Anweisung, diese sollte a zurückgeben const char * weil die Zeichenfolgen statische Konstanten sind: –

const char * FruitString(enum fruit e){

    unsigned int index;

    switch(e){
        CASE(fruit, apple)
        CASE(fruit, orange)
        CASE(fruit, banana)
        /* etc */
        default: index = ID_undefined;
    }
    return allEnums[index];
}

Bei der Programmierung mit Windows können die ID_-Werte Ressourcenwerte sein.

(Wenn Sie C++ verwenden, können alle Funktionen denselben Namen haben.

string EnumToString(fruit e);

)

1422360cookie-checkSo konvertieren Sie Aufzählungsnamen in Zeichenfolgen in c

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy