C enum'dan değer yerine metin yazdır


89

Beklediğiniz çıktınız "Pazar" vb. Dizeyi yazdırması mı?
GalacticCowboy

Yanıtlar:


104

C'deki numaralandırmalar, kodunuzun içinde uygun adlara sahip sayılardır. Bunlar dize değildir ve kaynak kodda kendilerine atanan adlar programınızda derlenmez ve bu nedenle çalışma zamanında erişilemezler.

İstediğinizi elde etmenin tek yolu, numaralandırma değerini bir dizeye çeviren bir işlevi kendiniz yazmaktır. Örneğin (burada beyanını enum Daysdışına taşıdığınızı varsayarsak main):

Alternatif olarak, bir diziyi harita olarak kullanabilirsiniz, ör.

Ama burada muhtemelen Sunday = 0numaralandırmanın güvenli olması için atamak isteyeceksiniz ... C standardının derleyicilerin numaralandırmaya 0'dan başlamasını gerektirip gerektirmediğinden emin değilim, ancak çoğu öyle (eminim ki birisi bunu onaylamak veya reddetmek için yorum yapacaktır. ).


3
Aw, beni dizi çözümünde yendin. : P Ama evet, farklı bir değer belirtmediğiniz sürece numaralandırmalar her zaman 0'dan başlar.
kazablanka

1
Numaralandırmaları dizin olarak kullanmaya güveniyor olsaydım, aslında her birini açık bir şekilde numaralandırmayı tercih ederdim. Standartlara göre gereksiz, ancak bir grup olarak derleyiciler, deneyimlerime göre standartları takip etmede tam olarak en iyisi değildi.
jdmichal

3
C standardı, "İlk numaralandırıcıda no = yoksa, numaralandırma sabitinin değeri 0'dır" der. Ancak açıkça belirtilmesi hiçbir şeye zarar vermez.
Michael Burr

17
C99 ile yapabileceğinizi unutmayın const char* dayNames[] = {[Sunday] = "Sunday", [Monday] = "Monday", [Tuesday] = "Tuesday", /* ... etc ... */ };. Bilirsiniz, haftanın günleri yeniden düzenlenirse veya Pazartesi gününün haftanın ilk günü olduğuna karar verirsiniz.
Tim Schaeffer

2
@ user3467349 Bu (önişlemci dizgisi) # işaretinden sonra gelen sembolü bir dizeye dönüştürür. Yani evet, #Monday "Pazartesi" ye dönüşecek ama Days TheDay = Monday; printf("%s", #TheDay);"TheDay" yazacaktır.
Tyler McHenry

29

Bunun gibi bir şey kullanıyorum:

"EnumToString.h" dosyasında:

daha sonra herhangi bir başlık dosyasında enum bildirimini yaparsınız, day enum.h

daha sonra EnumToString.c adlı bir dosyada:

sonra main.c:

bu, bu şekilde bildirilen ve "EnumToString.c" içine dahil edilen tüm numaralandırmalar için dizeleri "otomatik olarak" oluşturur.


4
Baştan sona okumak çirkin, ancak veri çoğaltmanız yok. (Herkesin aksine.) Bundan hoşlanıp hoşlanmama konusunda kararsızım.
Kim Reece

1
Veri tekrarı içermeyen ve muhtemelen en iyi sürdürülebilirlik / esneklik ile olağanüstü yaratıcı çözüm için +1, ama yech! Sanırım yine de const char * [] yoluna gitmeyi tercih ederim.
bildiri

4
Evet, sürdürülebilirlik harika! Haftanın günleri değiştiğinde güncelleme yapmak gerçekten çok kolay! </sarcasm> Bu arada, programdaki İngilizce dizeler ve isimler arasındaki eşleştirme artık tekrarlamadan kaçınmak için sabit kodlandığından, bu yerelleştirme amaçları için bile yararlı değil. En azından diğer yaklaşımlarla, kaynak dosyalardaki her örneği değiştirmeden dizeleri çevirmek mümkündür.
R .. GitHub BUZA YARDIM ETMEYİ DURDUR

1
Muhtemelen dönüş ifadelerini return _(#element)ve benzerlerini değiştirerek (gettext gibi bir şeyle) onu uluslararasılaştırabilirsiniz .
Vargas

C ön işlemcisi bu kadar yararlı ancak bu kadar çirkin olduğunda, genellikle onu basit bir kod oluşturucu veya bir betik dilinde özel bir ön işlemciyle değiştiririm. Ve aslında, birden fazla projede tam olarak bu amaçla kullandığım bir Python betiğim var. Ancak bugünlerde bunu çok sık kullanmıyorum - birçok kullanım durumu için sadece dizeleri kullanmaktan ve numaralandırmalarla uğraşmaktan kurtulabilirsiniz (ve hatta C ++ veya ObjC'de daha da fazlası).
abarnert

7

Bunu genellikle yapmamın yolu, dize temsillerini aynı sırayla ayrı bir dizide depolamak ve ardından diziyi enum değeriyle indekslemektir:


4

enumC'deki s, beklediğiniz gibi çalışmıyor. Bunları bir çeşit yüceltilmiş sabitler gibi düşünebilirsiniz (bu tür sabitlerin bir toplamı olmakla ilgili birkaç ek fayda ile ) ve "Pazar" için yazdığınız metin, derleme sırasında gerçekten bir sayıya çözülür, metin şu şekildedir: sonuçta atılır.

Kısacası: gerçekten istediğiniz şeyi yapmak için dizelerin bir dizisini tutmanız veya enum değerinden yazdırmak istediğiniz metne eşlemek için bir işlev oluşturmanız gerekir.


4

C'deki numaralandırmalar, otomatik olarak sıralı tam sayı değerlerinin adlandırılmış listeleri için temelde sözdizimsel şekerdir. Yani, bu koda sahip olduğunuzda:

Derleyiciniz aslında şunu söyler:

Bu nedenle, bir C numaralandırmasını bir dizge olarak çıkarmak, derleyiciye mantıklı gelen bir işlem değildir. Bunlar için insan tarafından okunabilir dizelere sahip olmak istiyorsanız, numaralandırmalardan dizelere dönüştürmek için işlevler tanımlamanız gerekecektir.


4

Bunu makrolarla yapmanın daha net bir yolu:

Bunun tamamen taşınabilir b / w önişlemciler olduğundan emin değilim, ancak gcc ile çalışıyor.

Bu c99 btw'dir, yani c99 strictonu (çevrimiçi derleyici) ideone'a bağlarsanız kullanın .


Makroların ne kadar "temiz" olduğunu sevmelisiniz :-).
mk12

3

Partiye geç kaldığımı biliyorum ama buna ne dersin?

Bu şekilde, enumve char*dizisini manuel olarak senkronize etmeniz gerekmez . Eğer benim gibiyseniz, şansınız daha sonra değiştireceksiniz enumve char*dizi geçersiz dizeler yazdıracak. Bu, evrensel olarak desteklenen bir özellik olmayabilir. Ancak afaik, modern gün C derleyicilerinin çoğu bu belirlenmiş başlangıç ​​stilini destekliyor.

Burada belirlenmiş başlatıcılar hakkında daha fazla bilgi edinebilirsiniz .


1

Asıl soru, adı sadece bir kez yazmak istiyorsun.
Bunun gibi bir iderim var:

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

enum eklemek için bir yapı kullanır, böylece dizeye bir yazıcının her bir enum değeri tanımlamasını takip edebilir.

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

Numaralandırmanın farkı, sayılar tekrarlanırsa oluşturucu hatayı bildiremez. Numarayı yazmayı sevmiyorsanız, __LINE__değiştirebilir:

#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


0

bu konuda yeniyim ama bir anahtar ifadesi kesinlikle işe yarayacak


Doğru biçimlendirme (okuma: girinti) cevaplardaki kelimesi kelimesine kod için bir zorunluluktur ...
p4010

0

Bunun dayNames içinde numaralandırmasını seviyorum. Yazmayı azaltmak için şunları yapabiliriz:


0

Başka bir çözüm var: Kendi dinamik numaralandırma sınıfınızı oluşturun. structYeni bir numaralandırma oluşturmak için bir ve bazı işlevlere sahip olduğunuz anlamına gelir , bu, öğeleri bir içinde saklayan structve her öğenin ad için bir dizesi vardır. Ayrıca, tek tek öğeleri saklamak için bir türe, bunları karşılaştırmak için işlevlere vb. İhtiyacınız var. İşte bir örnek:

Numaralandırmanın işlevleri kendi çeviri birimlerinde olmalıdır, ancak daha basit hale getirmek için onları burada birleştirdim.

Avantajı, bu çözümün esnek olması, KURU ilkesini takip etmesi, her bir öğeyle birlikte bilgileri depolayabilmeniz, çalışma süresi boyunca yeni numaralandırmalar oluşturabilmeniz ve çalışma süresi sırasında yeni öğeler ekleyebilmenizdir. Dezavantajı kullanılamaz, bu kompleks olmasıdır dinamik bellek ayırma ihtiyacı switch- case, fazla bellek gereksinimi ve daha yavaştır. Soru, buna ihtiyaç duyduğunuz durumlarda daha yüksek seviyeli bir dil kullanmamalısınız.


-3

TheDay, bazı tamsayı türlerine geri döner. Yani:

TheDay'i bir dize olarak ayrıştırmaya çalışır ve ya çöp ya da kilitlenme yazdırır.

printf tip güvenli değildir ve doğru değeri ona aktaracağınıza güvenir. Değerin adını yazdırmak için, enum değerini bir dizeye eşlemek için bir yöntem oluşturmanız gerekir - bir arama tablosu, dev bir anahtar ifadesi vb.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.