Wie konvertiert man ein Byte-Array in eine hexadezimale Zeichenfolge in C?

Lesezeit: 9 Minuten

Benutzeravatar von Steve Walsh
Steve Walsch

Ich habe:

uint8 buf[] = {0, 1, 10, 11};

Ich möchte das Byte-Array in einen String konvertieren, damit ich den String mit printf drucken kann:

printf("%s\n", str);

und erhalten (die Doppelpunkte sind nicht erforderlich):

"00:01:0A:0B"

Jede Hilfe wäre sehr willkommen.

  • buf[i] muss gecastet werden unsigned charoder es wird überlaufen, wenn buf[i] > 127das ist:buf_ptr += sprintf(buf_ptr, "%02X", (unsigned char)buf[i]);

    – wie kalt

    6. März 2017 um 14:55 Uhr


Benutzeravatar von Mark Synowiec
Markus Synowiec

printf("%02X:%02X:%02X:%02X", buf[0], buf[1], buf[2], buf[3]);

Für einen allgemeineren Weg:

int i;
for (i = 0; i < x; i++)
{
    if (i > 0) printf(":");
    printf("%02X", buf[i]);
}
printf("\n");

Um eine Zeichenfolge zu verketten, gibt es mehrere Möglichkeiten, dies zu tun. Ich würde wahrscheinlich einen Zeiger auf das Ende der Zeichenfolge behalten und sprintf verwenden. Sie sollten auch die Größe des Arrays im Auge behalten, um sicherzustellen, dass es nicht größer wird als der zugewiesene Speicherplatz:

int i;
char* buf2 = stringbuf;
char* endofbuf = stringbuf + sizeof(stringbuf);
for (i = 0; i < x; i++)
{
    /* i use 5 here since we are going to add at most 
       3 chars, need a space for the end '\n' and need
       a null terminator */
    if (buf2 + 5 < endofbuf)
    {
        if (i > 0)
        {
            buf2 += sprintf(buf2, ":");
        }
        buf2 += sprintf(buf2, "%02X", buf[i]);
    }
}
buf2 += sprintf(buf2, "\n");

  • Danke Mark – mein Problem ist etwas komplizierter. Ich habe tatsächlich einen Puffer mit einer Länge von X Bytes. Ich hatte gehofft, einen generischen Weg zu finden, dies für X Bytes zu tun und eine Zeichenfolge als Ergebnis zu haben.

    – Steve Walsh

    15. Juni 2011 um 11:39 Uhr

  • Nur aktualisiert, um Code für die Behandlung einer bestimmten Anzahl von Bytes hinzuzufügen … vorausgesetzt, x ist die Länge.

    – Markus Synowiec

    15. Juni 2011 um 11:44 Uhr

  • Nochmals vielen Dank Mark, aber was ich bei diesem Problem am schwierigsten fand, war, wie man dies in eine Zeichenfolge druckt.

    – Steve Walsh

    15. Juni 2011 um 11:47 Uhr

  • printf("%02X", (unsigned char)buf[i]); sollte verwendet werden, da das Original einen Überlauf für unsigned chars verursacht

    – ruhig Tiger

    8. Juli 2013 um 10:11 Uhr

  • Warum nicht printf("%02hhX", buf[i])?

    – Hintron

    17. April 2017 um 19:41 Uhr


Benutzeravatar von kriss
kriss

Der Vollständigkeit halber können Sie dies auch problemlos tun, ohne eine schwere Bibliotheksfunktion aufzurufen (kein snprintf, kein strcat, nicht einmal memcpy). Es kann nützlich sein, wenn Sie beispielsweise einen Mikrocontroller oder einen Betriebssystemkern programmieren, für den libc nicht verfügbar ist.

Nichts wirklich Besonderes, Sie können ähnlichen Code finden, wenn Sie danach googeln. Wirklich, es ist nicht viel komplizierter als das Aufrufen von snprintf und viel schneller.

#include <stdio.h>

int main(){
    unsigned char buf[] = {0, 1, 10, 11};
    /* target buffer should be large enough */
    char str[12];

    unsigned char * pin = buf;
    const char * hex = "0123456789ABCDEF";
    char * pout = str;
    int i = 0;
    for(; i < sizeof(buf)-1; ++i){
        *pout++ = hex[(*pin>>4)&0xF];
        *pout++ = hex[(*pin++)&0xF];
        *pout++ = ':';
    }
    *pout++ = hex[(*pin>>4)&0xF];
    *pout++ = hex[(*pin)&0xF];
    *pout = 0;

    printf("%s\n", str);
}

Hier ist eine weitere etwas kürzere Version. Es vermeidet lediglich die Zwischenindexvariable i und das Duplizieren des letzten Fallcodes (aber das abschließende Zeichen wird zweimal geschrieben).

#include <stdio.h>
int main(){
    unsigned char buf[] = {0, 1, 10, 11};
    /* target buffer should be large enough */
    char str[12];

    unsigned char * pin = buf;
    const char * hex = "0123456789ABCDEF";
    char * pout = str;
    for(; pin < buf+sizeof(buf); pout+=3, pin++){
        pout[0] = hex[(*pin>>4) & 0xF];
        pout[1] = hex[ *pin     & 0xF];
        pout[2] = ':';
    }
    pout[-1] = 0;

    printf("%s\n", str);
}

Unten ist noch eine andere Version, um auf einen Kommentar zu antworten, der besagt, dass ich einen “Trick” verwendet habe, um die Größe des Eingabepuffers zu kennen. Eigentlich ist es kein Trick, sondern ein notwendiges Eingabewissen (Sie müssen die Größe der Daten kennen, die Sie konvertieren). Ich habe dies klarer gemacht, indem ich den Konvertierungscode in eine separate Funktion extrahiert habe. Ich habe auch Grenzüberprüfungscode für den Zielpuffer hinzugefügt, was nicht wirklich notwendig ist, wenn wir wissen, was wir tun.

#include <stdio.h>

void tohex(unsigned char * in, size_t insz, char * out, size_t outsz)
{
    unsigned char * pin = in;
    const char * hex = "0123456789ABCDEF";
    char * pout = out;
    for(; pin < in+insz; pout +=3, pin++){
        pout[0] = hex[(*pin>>4) & 0xF];
        pout[1] = hex[ *pin     & 0xF];
        pout[2] = ':';
        if (pout + 3 - out > outsz){
            /* Better to truncate output string than overflow buffer */
            /* it would be still better to either return a status */
            /* or ensure the target buffer is large enough and it never happen */
            break;
        }
    }
    pout[-1] = 0;
}

int main(){
    enum {insz = 4, outsz = 3*insz};
    unsigned char buf[] = {0, 1, 10, 11};
    char str[outsz];
    tohex(buf, insz, str, outsz);
    printf("%s\n", str);
}

  • Es ist kein Trick, nur eine Konstante. Im Zusammenhang mit der Frage ist klar, dass die Länge der Quelle, die wir in Hexadezimal umwandeln möchten, bekannt ist (ich hätte anstelle von sizeof eine fest codierte 4 setzen können). Im allgemeinen Fall sollte die Funktion auf einer Eingabe bekannter Länge aufgerufen werden und der Zielpuffer 3 Mal + 1 Bytes verfügbar haben. Dies muss vom Aufrufer sichergestellt werden, es gibt keinen Grund für die Konvertierungsfunktion, diese Aufgabe zu übernehmen. Der Aufruf von strlen() kann in einigen Fällen eine Möglichkeit sein, die Quellgröße zu finden, aber nicht immer. Was ist, wenn die in Hex umzuwandelnde Zahl Nullen enthält?

    – kriss

    4. Februar 2015 um 17:32 Uhr


  • Inspiriert von Ihrer Funktion habe ich eine Version geschrieben, die auch die Anzahl der in den Ausgabepuffer geschriebenen Bytes zurückgibt, ähnlich wie snprintf usw. gist.github.com/cellularmitosis/0d8c0abf7f8aa6a2dff3

    – Jason Pepas

    26. September 2015 um 21:23 Uhr

  • Ich denke, Sie sollten Ihren Ausgabepuffer mit char str automatisch auf die richtige Größe bringen[ sizeof(buf)*3 + 1 ];

    – Cecil Ward

    31. Juli 2017 um 11:58 Uhr


  • Auch viel mehr Consts würden Sie schützen. ZB “const unsigned char const * p”, damit Sie sicherstellen können, dass Eingabepuffer nicht beschrieben werden. Einer macht die Adresse (oder den “Zeiger”) konstant oder variabel, und der andere macht den Speicher an dieser Adresse schreibgeschützt oder nicht. Hält Sie oft davon ab, die Zeiger zu verwechseln. Und auch aussagekräftige Namen zu haben, die dokumentieren, welche Puffer und Zeiger für die Ein- und Ausgabe bestimmt sind, würde ebenfalls helfen.

    – Cecil Ward

    31. Juli 2017 um 12:01 Uhr


  • @Cecil War: Wenn mein Code nicht falsch ist, schützt die Verwendung von const nicht viel, außer wie Sie sagen, dass Sie Zeiger verwechseln oder denselben Zeiger für Eingabe und Ausgabe verwenden (ok, immer noch möglich). Aber es hilft dem Compiler auch, den Code zu optimieren. Noch besser wäre es, auch das Schlüsselwort „restrict“ zu verwenden (schade, dass C99 nicht C++ ist, aber oft als Compiler-Erweiterung existiert). Was wollen Sie sinnvoller, als den Eingangspuffer aufzurufen in und Ausgangspuffer out ? Ich könnte mich auch dafür entscheiden, eine Zeichenfolge zu verwenden und eine Kopie zurückzugeben, anstatt einen Ausgabepuffer bereitzustellen, in modernen C++-Optimierern sind sie gut genug, um sich nicht viel darum zu kümmern.

    – kriss

    31. Juli 2017 um 15:51 Uhr

Benutzeravatar von razz
rasieren

Ähnliche Antworten gibt es bereits oben, ich habe diese hinzugefügt, um zu erklären, wie die folgende Codezeile genau funktioniert:

ptr += sprintf(ptr, "%02X", buf[i])

Es ist ziemlich knifflig und nicht leicht zu verstehen, ich habe die Erklärung in den Kommentaren unten eingefügt:

uint8 buf[] = {0, 1, 10, 11};

/* Allocate twice the number of bytes in the "buf" array because each byte would
 * be converted to two hex characters, also add an extra space for the terminating
 * null byte.
 * [size] is the size of the buf array */
char output[(size * 2) + 1];

/* pointer to the first item (0 index) of the output array */
char *ptr = &output[0];

int i;

for (i = 0; i < size; i++) {
    /* "sprintf" converts each byte in the "buf" array into a 2 hex string
     * characters appended with a null byte, for example 10 => "0A\0".
     *
     * This string would then be added to the output array starting from the
     * position pointed at by "ptr". For example if "ptr" is pointing at the 0
     * index then "0A\0" would be written as output[0] = '0', output[1] = 'A' and
     * output[2] = '\0'.
     *
     * "sprintf" returns the number of chars in its output excluding the null
     * byte, in our case this would be 2. So we move the "ptr" location two
     * steps ahead so that the next hex string would be written at the new
     * location, overriding the null byte from the previous hex string.
     *
     * We don't need to add a terminating null byte because it's been already 
     * added for us from the last hex string. */  
    ptr += sprintf(ptr, "%02X", buf[i]);
}

printf("%s\n", output);

  • Geniale Logik. Habe eine Stunde lang nach einer eleganten Nicht-C++-String-Antwort auf diese Herausforderung gesucht!

    – Mark Terrill

    24. Oktober 2019 um 5:40 Uhr

Benutzeravatar von Yannuth
Yannuth

Hier ist eine Methode, die viel schneller ist:

#include <stdlib.h>
#include <stdio.h>

unsigned char *     bin_to_strhex(const unsigned char *bin, unsigned int binsz,
                                  unsigned char **result)
{
  unsigned char     hex_str[]= "0123456789abcdef";
  unsigned int      i;

  if (!(*result = (unsigned char *)malloc(binsz * 2 + 1)))
    return (NULL);

  (*result)[binsz * 2] = 0;

  if (!binsz)
    return (NULL);

  for (i = 0; i < binsz; i++)
    {
      (*result)[i * 2 + 0] = hex_str[(bin[i] >> 4) & 0x0F];
      (*result)[i * 2 + 1] = hex_str[(bin[i]     ) & 0x0F];
    }
  return (*result);
}

int                 main()
{
  //the calling
  unsigned char     buf[] = {0,1,10,11};
  unsigned char *   result;

  printf("result : %s\n", bin_to_strhex((unsigned char *)buf, sizeof(buf), &result));
  free(result);

  return 0
}

Lösung

Funktion btox konvertiert beliebige Daten *bb zu einer nicht abgeschlossenen Zeichenfolge *xp von n Hexadezimalziffern:

void btox(char *xp, const char *bb, int n) 
{
    const char xx[]= "0123456789ABCDEF";
    while (--n >= 0) xp[n] = xx[(bb[n>>1] >> ((1 - (n&1)) << 2)) & 0xF];
}

Beispiel

#include <stdio.h>

typedef unsigned char uint8;

void main(void) 
{
    uint8 buf[] = {0, 1, 10, 11};
    int n = sizeof buf << 1;
    char hexstr[n + 1];

    btox(hexstr, buf, n);
    hexstr[n] = 0; /* Terminate! */
    printf("%s\n", hexstr);
}

Ergebnis: 00010A0B.

Live: Tio.run.

  • Benennung könnte besser sein

    – Mächtiger Wille

    10. August um 8:14 Uhr

Benutzeravatar von sdaau
sdaau

Ich wollte nur Folgendes hinzufügen, auch wenn es etwas vom Thema abweicht (nicht Standard-C), aber ich suche oft danach und stolpere über diese Frage unter den ersten Suchtreffern. Die Druckfunktion des Linux-Kernels, printkhat auch Formatbezeichner für die Ausgabe von Array-/Speicherinhalten “direkt” über einen einzelnen Formatbezeichner:

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

Raw buffer as a hex string:
    %*ph    00 01 02  ...  3f
    %*phC   00:01:02: ... :3f
    %*phD   00-01-02- ... -3f
    %*phN   000102 ... 3f

    For printing a small buffers (up to 64 bytes long) as a hex string with
    certain separator. For the larger buffers consider to use
    print_hex_dump(). 

… diese Formatbezeichner scheinen jedoch für den Standard-Benutzerbereich nicht zu existieren (s)printf.

  • Benennung könnte besser sein

    – Mächtiger Wille

    10. August um 8:14 Uhr

Benutzeravatar von forsvarir
fürsvarir

Dies ist eine Möglichkeit, die Konvertierung durchzuführen:

#include<stdio.h>
#include<stdlib.h>

#define l_word 15
#define u_word 240

char *hex_str[]={"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};

main(int argc,char *argv[]) {


     char *str = malloc(50);
     char *tmp;
     char *tmp2;

     int i=0;


     while( i < (argc-1)) {
          tmp = hex_str[*(argv[i]) & l_word];
          tmp2 = hex_str[*(argv[i]) & u_word];

          if(i == 0) { memcpy(str,tmp2,1); strcat(str,tmp);}
          else { strcat(str,tmp2); strcat(str,tmp);}
          i++;
    }

    printf("\n*********  %s  *************** \n", str);

}

1421670cookie-checkWie konvertiert man ein Byte-Array in eine hexadezimale Zeichenfolge in C?

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

Privacy policy