Bir bayt dizisini C'de onaltılık dizeye nasıl dönüştürürsünüz?


89

Sahibim:

Bayt dizisini, dizeyi printf kullanarak yazdırabileceğim bir dizeye dönüştürmek istiyorum:

and get (iki nokta üst üste gerekli değildir):

Herhangi bir yardım çok takdir edilecektir.


buf[i]atılmalıdır unsigned char, yoksa taşacak buf[i] > 127, yani:buf_ptr += sprintf(buf_ptr, "%02X", (unsigned char)buf[i]);
17:55

Yanıtlar:


93

daha genel bir yol için:

Bir dizgeyi birleştirmek için bunu yapmanın birkaç yolu vardır ... muhtemelen dizenin sonuna bir işaretçi tutar ve sprintf kullanırdım. Ayrılan alandan daha büyük olmadığından emin olmak için dizinin boyutunu da takip etmelisiniz:


Teşekkürler Mark - benim sorunum biraz daha karmaşık. Aslında X bayt uzunluğunda bir tamponum var. Bunu X bayt için yapmanın ve sonuç olarak bir dizeye sahip olmanın genel bir yolunu bulmayı umuyordum.
Steve Walsh

Belirli sayıda baytı işlemek için kod eklemek için güncellendi ... x'in uzunluk olduğu varsayılarak.
Mark Synowiec

Tekrar teşekkürler Mark, ama bu problem için en zor bulduğum şey, bunu bir dizeye nasıl yazdıracağım.
Steve Walsh

6
printf("%02X", (unsigned char)buf[i]);orijinal işaretsiz karakterler için taşmaya neden olacağından kullanılmalıdır
easytiger

3
Neden olmasın printf("%02hhX", buf[i])?
Hintron

32

Tamlık için, herhangi bir ağır kütüphane işlevini çağırmadan da kolayca yapabilirsiniz (snprintf, strcat, hatta memcpy yok). Örneğin, libc'nin bulunmadığı bir yerde mikro denetleyici veya işletim sistemi çekirdeği programlıyorsanız yararlı olabilir.

Eğer bunun için Google'da ararsanız, etrafta benzer bir kod bulamazsınız. Gerçekten, snprintf'i aramaktan çok daha karmaşık ve çok daha hızlı değil.

İşte biraz daha kısa bir versiyon. Sadece ara indeks değişkeni i'den ve son durum kodunu çoğaltmaktan kaçınır (ancak sonlandırma karakteri iki kez yazılır).

Aşağıda, giriş arabelleğinin boyutunu bilmek için bir "numara" kullandığımı söyleyen bir yoruma yanıt verecek başka bir sürüm daha var. Aslında bu bir numara değil, gerekli bir girdi bilgisi (dönüştürdüğünüz verinin boyutunu bilmeniz gerekir). Bunu, dönüştürme kodunu ayrı bir işleve çıkararak daha net hale getirdim. Ayrıca, ne yaptığımızı biliyorsak gerçekten gerekli olmayan hedef tampon için sınır kontrol kodu ekledim.


1
Bu bir numara değil, sadece bir sabit. Soru bağlamında, onaltılık tabana dönüştürmek istediğimiz kaynağın uzunluğunun iyi bilindiği açıktır (sizeof yerine bazı sabit kodlu 4 koyabilirdim). Genel durumda, işlev bilinen uzunluktaki bazı girdilerde çağrılmalıdır ve hedef tamponda 3 kez + 1 bayt kullanılabilir. Bu, arayan tarafından sağlanmalıdır, dönüştürme işlevinin bu görevi gerçekleştirmesi için hiçbir neden yoktur. Strlen () 'i çağırmak, her zaman olmamakla birlikte bazı durumlarda kaynak boyutunu bulmanın bir yolu olabilir. Ya hex'e dönüştürülecek sayı sıfır içeriyorsa?
kriss

İşlevinizden esinlenerek, çıktı arabelleğine yazılan bayt sayısını da döndüren bir sürüm yazdım, snprintf gibi. Gist.github.com/cellularmitosis/0d8c0abf7f8aa6a2dff3
Jason Pepas

Char str [sizeof (buf) * 3 + 1] kullanarak çıktı tamponunuzu otomatik olarak doğru boyutta yapmanız gerektiğini düşünüyorum;
Cecil Ward

Ayrıca çok daha fazla kullanıcı sizi koruyacaktır. Örneğin, "const unsigned char const * p", böylece girdi tamponlarının yazılmadığından emin olabilirsiniz. Biri adresi (veya 'gösterici') sabit veya değişken yapar ve diğeri bu adresteki belleği salt okunur yapar veya etmez. Genellikle, işaretçileri karıştırmanıza engel olur. Ayrıca, hangi tamponların ve işaretleyicilerin girdi ve çıktı için olduğunu belgeleyen anlamlı isimlere sahip olmak da yardımcı olacaktır.
Cecil Ward

@Cecil War: Kodum sahte olmadığı sürece const kullanmak, işaretçileri karıştırmak veya girdi ve çıktı için aynı işaretçiyi kullanmak dışında pek bir şey korumaz (tamam, yine de mümkün). Ancak derleyicinin kodu optimize etmesine de yardımcı olacaktır. Daha da iyisi, restrict anahtar sözcüğünü kullanmaktır (çok kötü C99, C ++ değil, ancak genellikle bir derleyici uzantısı olarak mevcuttur). Girdi arabelleğini inve çıktı arabelleğini çağırmaktan daha anlamlı ne istiyorsunuz out? Ayrıca bir dizge kullanmayı ve çıktı tamponu sağlamak yerine bir kopya döndürmeyi tercih edebilirim, modern C ++ 'da iyileştiriciler pek umursamayacak kadar iyidir.
kriss

15

İşte çok daha hızlı bir yöntem:


3
Bu kod, kendisini yalnızca yazdırılamayan garip girişlerde gösteren bir hata içerir (matematiksel olarak neler olup bittiğini tam olarak araştırmak için zamanım olmadı). Onaltılı ikilisini kodlamaya çalışın ca9e3c972f1c5db40c0b4a66ab5bc1a20ca4457bdbe5e0f8925896d5ed37d726ve çıkacaksınız ÌaÌe3cÌ72f1c5dÌ40c0b4a66Ìb5bÌ1Ì20cÌ4457bÌbÌ5Ì0Ì8Ì258Ì6Ì5Ìd37Ì726. Bunu düzeltmek hex_striçin, for döngüsünün ilk satırındaki bitin (input[i] >> 4) & 0x0F@ kriss'in cevabındaki gibi değiştirilmesi gerekir . O zaman iyi çalışıyor.
niemiro

Hata - malloc () hatasını kontrol etmez.
Cecil Ward

Hiç kimse imzalı karakter riskini istemediğinden (çılgın bir DEC PDP11 donanım özelliği) kesinlikle her yerde imzasız karakter kullanmak daha iyidir ve bu şekilde imzalı karşılaştırmaların yanlış gitmesi veya imzalı doğru vardiyaların değerleri bozması riskini almazsınız. Bu durumda, adil olmak gerekirse, kod, sizi burada koruyan her yerde savunmaya yönelik bir & 0x0F yapar.
Cecil Ward

Bu yordamın amaçları doğrultusunda belleği salt okunur olarak bildirmek için, bin input parametresi const unsigned char const * bin olmalıdır.
Cecil Ward

1
Cecil Ward'un önerilerini entegre ettim, geri bildirim için teşekkürler
Yannuth

14

Benzer cevaplar yukarıda zaten var, bunu aşağıdaki kod satırının tam olarak nasıl çalıştığını açıklamak için ekledim:

Oldukça karmaşık ve anlaşılması kolay değil, açıklamayı aşağıdaki yorumlara ekledim:


Harika mantık. Bu meydan okumaya zarif bir C ++ olmayan dizge cevabı için bir saat arıyordu!
Mark Terrill

6

Biraz konu dışı olsa da (standart C değil) aşağıdakileri eklemek istedim, ancak kendimi sık sık aradığımı ve ilk arama isabetleri arasında bu soruyu tökezlediğimi buluyorum. Linux çekirdeği yazdırma işlevi, printkaynı zamanda tek bir biçim belirleyicisi aracılığıyla dizi / bellek içeriklerini "doğrudan" çıkarmak için biçim belirleyicilerine sahiptir:

https://www.kernel.org/doc/Documentation/printk-formats.txt

... ancak, bu biçim belirleyicileri standart, kullanıcı alanı için mevcut görünmüyor (s)printf.


6

Çözüm

Fonksiyon btoxkeyfi veriye dönüştürür *bbbir unterminated dizeye *xparasında nonaltılık basamak:

Misal

Sonuç: 00010A0B.

Canlı: Tio.run .


1

Bu, dönüşümü gerçekleştirmenin bir yoludur:


1

Biraz değiştirilmiş Yannith versiyonu. Sadece bir dönüş değeri olmasını seviyorum

typedef struct {
   size_t len;
   uint8_t *bytes;
} vdata;

char* vdata_get_hex(const vdata data)
{
   char hex_str[]= "0123456789abcdef";

   char* out;
   out = (char *)malloc(data.len * 2 + 1);
   (out)[data.len * 2] = 0;
   
   if (!data.len) return NULL;
   
   for (size_t i = 0; i < data.len; i++) {
      (out)[i * 2 + 0] = hex_str[(data.bytes[i] >> 4) & 0x0F];
      (out)[i * 2 + 1] = hex_str[(data.bytes[i]     ) & 0x0F];
   }
   return out;
}


1

Bu işlev, kullanıcının / arayanın onaltılık dizenin bir charactee dizisine / arabelleğe yerleştirilmesini istediği durumlarda uygundur. Bir karakter arabelleğindeki onaltılık dizeyle, kullanıcı / arayan, istediği herhangi bir yerde (örneğin bir dosyaya) görüntülemek veya kaydetmek için kendi makrosunu / işlevini kullanabilir. Bu işlev aynı zamanda arayanın her satıra koyacağı (onaltılık) bayt sayısını kontrol etmesine olanak tanır.

Örnek: Kod

ÇIKTI


0

C'de bunun için ilkel yoktur. Muhtemelen malloc (veya belki de alloca) yeterince uzun bir tampon ve girdinin üzerinde döngü oluştururum. Ayrıca C ++ 'lara benzer semantik (ama sözdizimi değil!) Dinamik bir dizgi kitaplığı ile yapıldığını gördüm ostringstream, bu makul bir şekilde daha genel bir çözümdür, ancak yalnızca tek bir durum için ekstra karmaşıklığa değmeyebilir.


0

Onaltılık değerleri bir char *dizede saklamak istiyorsanız kullanabilirsiniz snprintf. Baştaki sıfırlar ve iki nokta üst üste dahil olmak üzere tüm yazdırılan karakterler için alan ayırmanız gerekir.

Mark'ın cevabını genişleterek:

Şimdi str_bufonaltılık dizeyi içerecek.


bu ilk 2 karakterin üzerine tekrar tekrar yazar .. değil mi?
xordon

0

ZincX'in çözümü, iki nokta üst üste sınırlayıcıları içerecek şekilde uyarlanmıştır:


0

İlgilenen herkes için C ++ sürümünü buraya ekleyeceğim .

Bu bir HexDump yazdırır bufferuzunluğu countiçin std::ostream out(bunu varsayılan yapabilirsiniz std::cout). Her satır bytes_per_linebayt içerecektir , her bayt büyük harfli iki basamaklı onaltılık kullanılarak temsil edilir. Baytlar arasında boşluk olacak. Ve satırın sonunda veya arabelleğin sonunda bir satırsonu yazacaktır. bytes_per_line0 yapılırsa , yeni_satır yazdırmaz. Kendiniz deneyin.


0

Basit kullanım için, giriş dizesini (ikili veri) kodlayan bir işlev yaptım:

Kullanım:


0

Yannuth'un cevabına göre basitleştirilmiş.

Burada uzunluğunun dest[]iki katı olacağı ima edilir lenve tahsisi arayan tarafından yönetilir.


0

Bu sorunun zaten bir cevabı olduğunu biliyorum, ancak çözümümün birine yardımcı olabileceğini düşünüyorum.

Yani, benim durumumda anahtarı temsil eden bir bayt dizim vardı ve bu bayt dizisini bir satırda yazdırmak için onaltılık değerlerin char dizisine dönüştürmem gerekiyordu. Kodumu şöyle bir işleve çıkardım:

Şimdi, işlevimi şu şekilde kullanabilirim:

Serialobject, Arduino kütüphanesinin bir parçasıdır m_publicKeyve aşağıdaki bildirime sahip sınıfımın üyesidir uint8_t m_publicKey[32].


0

Snprintf ve malloc ile çözebilirsiniz.

ÇIKIŞ: bbccdd0fef0f0e0d0c


-2

Ne karmaşık çözümler!
Malloc ve sprintler ve yayınlar oh my. (OZ alıntı)
ve hiçbir yerde tek bir rem değil. Tanrım

böyle bir şeye ne dersin?


Tamam, şimdi iki onaltılık rakamınız var. Ayırıcılar eklemek ve dönüştürülecek diğer baytlarla ilgilenmek için kalır. Belki bir döngü ile? Bunu bir işlev haline getirin ve benimkine benzer bir şeye sahip olacaksınız (daha ziyade ayrıntılı ve okuması zor). Belki de en azından diğer afişlerdeki isimleri çağırmadan önce işi bitirmelisiniz?
kriss

1
Ve kaynak koddaki yorumlar hakkında bir kelime (REM değil, bu yorumlar için TEMEL anahtar kelimedir, lütfen bundan kaçının): kodun ne yaptığını İngilizce olarak söyleyen yorumlar çok kötü bir uygulamadır! Evet programcıların, modulo operatörlerinin ne anlama geldiğini (kalıntıları verdiklerini) ve bu bölme sayımının, bir sayının bir diğerinde kaç kez göründüğünü ... ve sonucu yazdırdığını bilmeleri gerekir. Aman!
kriss
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.