คุณจะแปลงอาร์เรย์ไบต์เป็นสตริงเลขฐานสิบหกใน C ได้อย่างไร?


89

ฉันมี:

ฉันต้องการแปลงอาร์เรย์ไบต์เป็นสตริงเพื่อที่ฉันจะสามารถพิมพ์สตริงโดยใช้ printf:

และรับ (ไม่จำเป็นต้องใช้เครื่องหมายทวิภาค):

ความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชมอย่างมาก.


buf[i]จะต้องถูกโยนไปunsigned charหรือมันจะล้นถ้าbuf[i] > 127นั่นคือ:buf_ptr += sprintf(buf_ptr, "%02X", (unsigned char)buf[i]);
whatacold

คำตอบ:


93

สำหรับวิธีทั่วไปเพิ่มเติม:

ในการเชื่อมต่อกับสตริงมีสองสามวิธีที่คุณสามารถทำได้ ... ฉันอาจจะให้ตัวชี้ไปที่จุดสิ้นสุดของสตริงและใช้ sprintf คุณควรติดตามขนาดของอาร์เรย์เพื่อให้แน่ใจว่าจะไม่ใหญ่กว่าพื้นที่ที่จัดสรร:


ขอบคุณมาร์ค - ปัญหาของฉันซับซ้อนกว่านี้เล็กน้อย ที่จริงฉันมีบัฟเฟอร์ที่มีความยาว X ไบต์ ฉันหวังว่าจะพบวิธีทั่วไปในการทำสิ่งนี้สำหรับ X ไบต์และมีสตริงเป็นผลลัพธ์
Steve Walsh

เพิ่งอัปเดตเพื่อเพิ่มรหัสสำหรับจัดการจำนวนไบต์ที่กำหนด ... สมมติว่า x คือความยาว
Mark Synowiec

ขอขอบคุณอีกครั้ง Mark แต่สิ่งที่ฉันพบว่ายุ่งยากที่สุดสำหรับปัญหานี้คือวิธีการพิมพ์ลงในสตริง
Steve Walsh

6
printf("%02X", (unsigned char)buf[i]);ควรใช้เป็นต้นฉบับจะทำให้ล้นสำหรับตัวอักษรที่ไม่ได้ลงชื่อ
easytiger

3
ทำไมไม่printf("%02hhX", buf[i])?
Hintron

32

เพื่อความสมบูรณ์คุณสามารถทำได้อย่างง่ายดายโดยไม่ต้องเรียกใช้ฟังก์ชันไลบรารีที่หนักหน่วง (ไม่มี snprintf ไม่มี strcat หรือแม้แต่ memcpy) อาจเป็นประโยชน์เช่นหากคุณกำลังเขียนโปรแกรมไมโครคอนโทรลเลอร์หรือเคอร์เนลระบบปฏิบัติการที่ libc ไม่พร้อมใช้งาน

ไม่มีอะไรน่าสนใจจริงๆที่คุณจะพบรหัสที่คล้ายกันหากคุณใช้ Google จริงๆแล้วมันไม่ซับซ้อนไปกว่าการเรียก snprintf และเร็วกว่ามาก

นี่คืออีกเวอร์ชันที่สั้นกว่าเล็กน้อย เป็นเพียงการหลีกเลี่ยงตัวแปรดัชนีกลาง i และการทำซ้ำรหัส laste case (แต่อักขระการยุติถูกเขียนสองครั้ง)

ด้านล่างนี้เป็นอีกเวอร์ชันสำหรับตอบความคิดเห็นที่บอกว่าฉันใช้ "เคล็ดลับ" เพื่อให้ทราบขนาดของบัฟเฟอร์อินพุต จริงๆแล้วมันไม่ใช่เคล็ดลับ แต่เป็นความรู้ในการป้อนข้อมูลที่จำเป็น (คุณต้องรู้ขนาดของข้อมูลที่คุณกำลังแปลง) ฉันทำให้สิ่งนี้ชัดเจนขึ้นโดยการแยกรหัสการแปลงเป็นฟังก์ชันแยกต่างหาก ฉันยังเพิ่มรหัสตรวจสอบขอบเขตสำหรับบัฟเฟอร์เป้าหมายซึ่งไม่จำเป็นจริงๆหากเรารู้ว่าเรากำลังทำอะไรอยู่


1
ไม่ใช่เคล็ดลับเป็นเพียงค่าคงที่ ในบริบทของคำถามนั้นชัดเจนว่าความยาวของแหล่งที่มาที่เราต้องการแปลงเป็นเลขฐานสิบหกนั้นเป็นที่รู้จักกันดี (ฉันสามารถใส่ฮาร์ดโค้ด 4 แทน sizeof ได้) ในกรณีทั่วไปควรเรียกใช้ฟังก์ชันกับอินพุตบางส่วนของความยาวที่ทราบและบัฟเฟอร์เป้าหมายมี 3 เท่า + 1 ไบต์พร้อมใช้งาน สิ่งนี้ต้องได้รับการรับรองจากผู้เรียกไม่มีเหตุผลที่ฟังก์ชันการแปลงจะดำเนินการนั้น การเรียก strlen () อาจเป็นวิธีค้นหาขนาดของแหล่งที่มาในบางกรณี แต่ก็ไม่เสมอไป จะเกิดอะไรขึ้นถ้าตัวเลขที่จะแปลงเป็นฐานสิบหกมีศูนย์?
kriss

แรงบันดาลใจจากฟังก์ชันของคุณฉันเขียนเวอร์ชันซึ่งส่งคืนจำนวนไบต์ที่เขียนไปยังบัฟเฟอร์เอาต์พุตเช่นเดียวกับ snprintf เป็นต้น gist.github.com/cellularmitosis/0d8c0abf7f8aa6a2dff3
Jason Pepas

ฉันคิดว่าคุณควรทำให้เอาต์พุตบัฟเฟอร์ของคุณมีขนาดที่ถูกต้องโดยอัตโนมัติโดยใช้ char str [sizeof (buf) * 3 + 1];
Cecil Ward

นอกจากนี้ consts อื่น ๆ อีกมากมายจะปกป้องคุณ เช่น "const ไม่ได้ลงนาม char const * p" เพื่อให้แน่ใจว่าบัฟเฟอร์อินพุตไม่ได้เขียนถึง หนึ่งทำให้ที่อยู่ (หรือ 'ตัวชี้') คงที่หรือตัวแปรและอีกตัวทำให้หน่วยความจำที่อยู่นั้นเป็นแบบอ่านอย่างเดียวหรือไม่ บ่อยครั้งที่คุณหยุดไม่ให้พอยน์เตอร์สับสน นอกจากนี้การมีชื่อที่มีความหมายซึ่งเอกสารที่บัฟเฟอร์และพอยน์เตอร์สำหรับอินพุตและเอาต์พุตจะช่วยได้เช่นกัน
Cecil Ward

@Cecil War: เว้นแต่รหัสของฉันจะปลอมโดยใช้ const จะไม่ป้องกันมากนักยกเว้นในขณะที่คุณพูดว่าผสมตัวชี้หรือใช้ตัวชี้เดียวกันสำหรับอินพุตและเอาต์พุต (ตกลงยังคงเป็นไปได้) แต่ยังช่วยคอมไพเลอร์ในการปรับแต่งโค้ด ที่ดีไปกว่านั้นคือการใช้คำหลักที่ จำกัด ด้วย (C99 ที่แย่เกินไปไม่ใช่ C ++ แต่มักมีอยู่เป็นส่วนขยายของคอมไพเลอร์) สิ่งที่คุณต้องการ meaninfull เพิ่มเติมเกี่ยวกับบัฟเฟอร์โทรinและบัฟเฟอร์ส่งออกout? ฉันยังสามารถเลือกใช้สตริงและส่งคืนสำเนาแทนการให้บัฟเฟอร์เอาต์พุตได้ในตัวเพิ่มประสิทธิภาพ C ++ สมัยใหม่นั้นดีพอที่จะไม่ต้องสนใจอะไรมาก
kriss

15

นี่คือวิธีการที่เร็วกว่า:


3
รหัสนี้มีจุดบกพร่องที่ปรากฏเฉพาะในอินพุตที่ไม่สามารถพิมพ์ได้แปลก ๆ (ไม่มีเวลาเจาะลึกว่าเกิดอะไรขึ้นทางคณิตศาสตร์) พยายามที่จะเข้ารหัสไบนารีของเลขฐานสิบหกca9e3c972f1c5db40c0b4a66ab5bc1a20ca4457bdbe5e0f8925896d5ed37d726และคุณจะได้รับÌaÌe3cÌ72f1c5dÌ40c0b4a66Ìb5bÌ1Ì20cÌ4457bÌbÌ5Ì0Ì8Ì258Ì6Ì5Ìd37Ì726จาก ในการแก้ไขปัญหานี้บิตที่อยู่hex_strในบรรทัดแรกของ for loop จำเป็นต้องเปลี่ยน(input[i] >> 4) & 0x0Fเป็นคำตอบของ @ kriss จากนั้นก็ใช้งานได้ดี
niemiro

ข้อผิดพลาด - ไม่ได้ตรวจสอบความล้มเหลวของ malloc ()
Cecil Ward

เป็นการดีกว่าเสมอที่จะใช้ถ่านที่ไม่ได้ลงชื่อทุกที่เนื่องจากไม่มีใครต้องการความเสี่ยงของตัวอักษรที่ลงนาม (คุณลักษณะฮาร์ดแวร์ DEC PDP11 ที่บ้าคลั่ง) และด้วยวิธีนี้คุณจะไม่เสี่ยงต่อการเปรียบเทียบที่ลงนามผิดพลาดหรือลงนามถูกต้องจะเปลี่ยนค่าที่เสียหาย ในกรณีนี้เพื่อความเป็นธรรมโค้ดจะทำ & 0x0F ในทุกที่ที่ปกป้องคุณที่นี่
Cecil Ward

พารามิเตอร์อินพุต bin ควรเป็น const char const * bin ที่ไม่ได้ลงนามเพื่อประกาศหน่วยความจำเป็นแบบอ่านอย่างเดียวสำหรับวัตถุประสงค์ของรูทีนนี้
Cecil Ward

1
ฉันได้รวมข้อเสนอของเซซิลวอร์ดแล้วขอบคุณสำหรับคำติชม
ยานนุ ธ

14

คำตอบที่คล้ายกันมีอยู่แล้วข้างต้นฉันได้เพิ่มคำตอบนี้เพื่ออธิบายว่าบรรทัดของโค้ดต่อไปนี้ทำงานอย่างไร:

เป็นเรื่องที่ค่อนข้างเงียบและเข้าใจได้ไม่ยากฉันใส่คำอธิบายไว้ในความคิดเห็นด้านล่าง:


ตรรกะที่ยอดเยี่ยม กำลังมองหาหนึ่งชั่วโมงสำหรับคำตอบสตริงที่ไม่ใช่ C ++ ที่สง่างามสำหรับความท้าทายนี้!
Mark Terrill

6

ฉันแค่อยากจะเพิ่มสิ่งต่อไปนี้แม้ว่าจะไม่ตรงประเด็นเล็กน้อย (ไม่ใช่มาตรฐาน C) แต่ฉันพบว่าตัวเองกำลังมองหาบ่อยครั้งและสะดุดกับคำถามนี้ในการค้นหาครั้งแรก ฟังก์ชันการพิมพ์เคอร์เนลของลินุกซ์printkยังมีตัวระบุรูปแบบสำหรับการส่งออกเนื้อหาอาร์เรย์ / หน่วยความจำ "โดยตรง" ผ่านตัวระบุรูปแบบเอกพจน์:

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

... แต่ specifiers (s)printfรูปแบบเหล่านี้ดูเหมือนจะไม่อยู่สำหรับมาตรฐานผู้ใช้พื้นที่


6

วิธีการแก้

ฟังก์ชันbtoxแปลงข้อมูลโดยพล*bbการเป็นสตริง*xpของnเลขฐานสิบหกที่ไม่สิ้นสุด:

ตัวอย่าง

ผลลัพธ์: 00010A0B.

สด: Tio.run .


1

นี่เป็นวิธีหนึ่งในการแปลง:


1

รุ่นยานนิ ธ แก้ไขเล็กน้อย เป็นเพียงแค่ฉันต้องการให้มันเป็นค่าตอบแทน

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

ฟังก์ชันนี้เหมาะสำหรับที่ผู้ใช้ / ผู้เรียกต้องการให้ใส่สตริงเลขฐานสิบหกในอาร์เรย์ / บัฟเฟอร์ของอักขระ ด้วยสตริงฐานสิบหกในบัฟเฟอร์อักขระผู้ใช้ / ผู้โทรสามารถใช้มาโคร / ฟังก์ชันของตัวเองเพื่อแสดงหรือบันทึกไปยังที่ใดก็ได้ที่ต้องการ (เช่นไฟล์) ฟังก์ชันนี้ยังช่วยให้ผู้โทรสามารถควบคุมจำนวน (hex) ไบต์ที่จะใส่ในแต่ละบรรทัด

ตัวอย่าง: รหัส

เอาท์พุท


0

ไม่มีแบบดั้งเดิมสำหรับสิ่งนี้ใน C. ฉันอาจจะ malloc (หรืออาจจะจัดสรร) บัฟเฟอร์ที่ยาวพอและวนซ้ำบนอินพุต ฉันเคยเห็นมันทำด้วยไลบรารีสตริงแบบไดนามิกที่มีความหมาย (แต่ไม่ใช่ไวยากรณ์!) คล้ายกับ C ++ ostringstreamซึ่งเป็นวิธีแก้ปัญหาทั่วไปที่น่าจะเป็นไปได้มากขึ้น แต่มันอาจไม่คุ้มกับความซับซ้อนพิเศษสำหรับกรณีเดียว


0

หากคุณต้องการจัดเก็บค่าฐานสิบหกในchar *สตริงคุณสามารถใช้snprintf. คุณต้องจัดสรรพื้นที่สำหรับอักขระที่พิมพ์ทั้งหมดรวมถึงเลขศูนย์และโคลอนนำหน้า

ขยายคำตอบของ Mark:

ตอนนี้str_bufจะมีสตริงฐานสิบหก


สิ่งนี้จะเขียนทับอักขระ 2 ตัวแรกซ้ำแล้วซ้ำเล่า .. จริงไหม?
xordon

0

โซลูชันของ ZincX ปรับให้รวมตัวคั่นลำไส้ใหญ่:


0

ฉันจะเพิ่มเวอร์ชันC ++ที่นี่สำหรับทุกคนที่สนใจ

มันจะพิมพ์ฐานสิบหกของbufferความยาวcountเป็นstd::ostream out(คุณสามารถกำหนดให้เป็นค่าเริ่มต้นได้std::cout) ทุกบรรทัดจะมีbytes_per_lineไบต์แต่ละไบต์จะแสดงโดยใช้ฐานสิบหกสองหลักตัวพิมพ์ใหญ่ จะมีช่องว่างระหว่างไบต์ และท้ายบรรทัดหรือท้ายบัฟเฟอร์จะพิมพ์ขึ้นบรรทัดใหม่ หากbytes_per_lineตั้งค่าเป็น 0 จะไม่พิมพ์ new_line ลองด้วยตัวคุณเอง


0

สำหรับการใช้งานอย่างง่ายฉันสร้างฟังก์ชันที่เข้ารหัสสตริงอินพุต (ข้อมูลไบนารี):

การใช้งาน:


0

อ้างอิงจากคำตอบของญาณยุทธแต่ทำให้เข้าใจง่าย

ที่นี่ความยาวdest[]โดยนัยจะเป็นสองเท่าของlenและการจัดสรรถูกจัดการโดยผู้โทร


0

ฉันรู้ว่าคำถามนี้มีคำตอบอยู่แล้ว แต่ฉันคิดว่าคำตอบของฉันสามารถช่วยใครบางคนได้

ดังนั้นในกรณีของฉันฉันมีอาร์เรย์ไบต์ที่เป็นตัวแทนของคีย์และฉันจำเป็นต้องแปลงอาร์เรย์ไบต์นี้เป็นอาร์เรย์ของค่าเลขฐานสิบหกเพื่อที่จะพิมพ์ออกมาในบรรทัดเดียว ฉันแตกรหัสของฉันเป็นฟังก์ชันดังนี้:

ตอนนี้ฉันสามารถใช้ฟังก์ชันของฉันได้ดังนี้:

Serialวัตถุที่เป็นส่วนหนึ่งของห้องสมุด Arduino และเป็นสมาชิกของชั้นเรียนของฉันกับการประกาศดังต่อไปนี้m_publicKeyuint8_t m_publicKey[32]


0

คุณแก้ได้ด้วย snprintf และ malloc

ออก: bbccdd0fef0f0e0d0c


-2

วิธีแก้ที่ซับซ้อนอะไร!
Malloc และ sprints และโยนโอ้ฉัน (OZ quote)
และไม่ใช่ rem เดียวที่ใดก็ได้ เอ้ย

อะไรแบบนี้?


ตกลงตอนนี้คุณมีเลขฐานสิบหกสองหลัก ยังคงเพิ่มตัวคั่นและดูแลไบต์อื่น ๆ ในการแปลง อาจจะมีห่วง? ทำให้มันเป็นฟังก์ชั่นและคุณจะมีสิ่งที่คล้ายกับของฉัน (แต่ค่อนข้างละเอียดและอ่านยาก) อย่างน้อยคุณควรทำงานให้เสร็จก่อนเรียกชื่อในโปสเตอร์อื่น ๆ ?
kriss

1
และคำเกี่ยวกับความคิดเห็นในซอร์สโค้ด (ไม่ใช่ REM นั่นคือคีย์เวิร์ดพื้นฐานสำหรับความคิดเห็นโปรดหลีกเลี่ยงสิ่งนั้น): ความคิดเห็นที่พูดเป็นภาษาอังกฤษว่าโค้ดกำลังทำอะไรอยู่เป็นการปฏิบัติที่แย่มาก! ใช่โปรแกรมเมอร์ควรจะรู้ว่าตัวดำเนินการโมดูโลหมายถึงอะไร (ให้ส่วนที่เหลือ) และการหารนั้นจะนับจำนวนครั้งที่ตัวเลขปรากฏในอีกอันหนึ่ง ... และ printf นั้นจะพิมพ์ผลลัพธ์ พุทโธ่!
kriss
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.