web-dev-qa-db-de.com

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

Ich habe:

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

Ich möchte das Byte-Array in eine Zeichenfolge konvertieren, sodass ich die Zeichenfolge mit printf drucken kann:

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

und erhalten (die Doppelpunkte sind nicht notwendig):

"00:01:0A:0B"

Jede Hilfe wäre sehr dankbar.

71
Steve Walsh
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 einige Möglichkeiten, wie Sie dies tun können. Sie sollten auch die Größe des Arrays überwachen, 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");
74
Mark Synowiec

Zum Abschluss können Sie dies auch ganz einfach tun, ohne eine umfangreiche Library-Funktion aufzurufen (kein snprintf, kein strcat, nicht einmal memcpy). Dies kann nützlich sein, wenn Sie beispielsweise einen Mikrocontroller oder einen OS-Kernel programmieren, für den libc nicht verfügbar ist. 

Nichts Besonderes, Sie können ähnlichen Code finden, wenn Sie dafür googeln. Es ist wirklich 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 andere, etwas kürzere Version. Es vermeidet lediglich die Zwischenindexvariable i und das Kopieren des Laste-Case-Codes (das abschließende Zeichen wird jedoch 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);
}

Nachfolgend finden Sie eine weitere Version, die auf einen Kommentar eingeht, der besagt, dass ich einen "Trick" verwendet habe, um die Größe des Eingangspuffers zu ermitteln. Eigentlich ist es kein Trick, sondern ein notwendiges Eingabewissen (Sie müssen die Größe der Daten, die Sie konvertieren, kennen). Ich habe dies klarer gemacht, indem ich den Konvertierungscode in eine separate Funktion extrahierte. Ich habe auch einen 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);
}
25
kriss

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
}
15
Yannuth

Ä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 kompliziert und nicht leicht zu verstehen, ich füge die Erklärung in den folgenden Kommentaren ein:

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

/* Allocate twice the number of the 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 to 2 chars hex string and a null byte, for example
     * 10 => "0A\0".
     *
     * These three chars would be added to the output array starting from
     * the ptr location, for example if ptr is pointing at 0 index then the hex chars
     * "0A\0" would be written as output[0] = '0', output[1] = 'A' and output[2] = '\0'.
     *
     * sprintf returns the number of chars written execluding the null byte, in our case
     * this would be 2. Then we move the ptr location two steps ahead so that the next
     * hex char would be written just after this one and overriding this one's null byte.
     *
     * We don't need to add a terminating null byte because it's already added from 
     * the last hex string. */  
    ptr += sprintf (ptr, "%02X", buf[i]);
}

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

Ich wollte nur Folgendes hinzufügen, auch wenn es ein wenig außerhalb des Themas (nicht Standard C) ist, aber ich finde es oft, und stolpere bei den ersten Suchtreffern über diese Frage. Die Linux-Kernel-Druckfunktion, printk, enthält auch Formatbezeichner für die direkte Ausgabe von Array-/Speicherinhalten über einen einzigen 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 Standardbenutzerraum (s)printf nicht zu existieren.

5
sdaau

Diese Funktion ist geeignet, wenn der Benutzer/Anrufer wünscht, dass der Hex-String in einen charakteristischen Array/Puffer eingefügt wird. Mit einem Hex-String in einem Zeichenpuffer kann der Benutzer/Anrufer sein eigenes Makro/seine eigene Funktion verwenden, um ihn an einem beliebigen Ort (z. B. in einer Datei) anzuzeigen oder zu protokollieren. Mit dieser Funktion kann der Anrufer auch die Anzahl der (Hex) Bytes steuern, die in jede Zeile eingefügt werden sollen.

/**
 * @fn 
 * get_hex
 *
 * @brief 
 * Converts a char into bunary string 
 *
 * @param[in]   
 *     buf Value to be converted to hex string
 * @param[in]   
 *     buf_len Length of the buffer
 * @param[in]   
 *     hex_ Pointer to space to put Hex string into
 * @param[in]   
 *     hex_len Length of the hex string space
 * @param[in]   
 *     num_col Number of columns in display hex string
 * @param[out]   
 *     hex_ Contains the hex string
 * @return  void
 */
static inline void
get_hex(char *buf, int buf_len, char* hex_, int hex_len, int num_col)
{
    int i;
#define ONE_BYTE_HEX_STRING_SIZE   3
  unsigned int byte_no = 0;

  if (buf_len <= 0) {
      if (hex_len > 0) {
        hex_[0] = '\0';
      }
      return;
  }

  if(hex_len < ONE_BYTE_HEX_STRING_SIZE + 1)
  {
      return;
  }

  do {
         for (i = 0; ((i < num_col) && (buf_len > 0) && (hex_len > 0)); ++i )
         {
            snprintf(hex_, hex_len, "%02X ", buf[byte_no++] & 0xff);
            hex_ += ONE_BYTE_HEX_STRING_SIZE;
            hex_len -=ONE_BYTE_HEX_STRING_SIZE;
            buf_len--;
         }
         if (buf_len > 1)
         {
             snprintf(hex_, hex_len, "\n");
             hex_ += 1;
         }
  } while ((buf_len) > 0 && (hex_len > 0));

}

Beispiel: Code

#define DATA_HEX_STR_LEN 5000
    char      data_hex_str[DATA_HEX_STR_LEN];

    get_hex(pkt, pkt_len, data_hex_str, DATA_HEX_STR_LEN, 16);
    //      ^^^^^^^^^^^^                                  ^^
    //      Input byte array                              Number of (hex) byte
    //      to be converted to hex string                 columns in hex string

    printf("pkt:\n%s",data_hex_str) 

AUSGABE

pkt:
BB 31 32 00 00 00 00 00 FF FF FF FF FF FF DE E5 
A8 E2 8E C1 08 06 00 01 08 00 06 04 00 01 DE E5 
A8 E2 8E C1 67 1E 5A 02 00 00 00 00 00 00 67 1E 
5A 01 
1

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);

}
1
rajaganesh

Lösung

Die Funktion btox konvertiert beliebige Daten *bb in eine nicht abgeschlossene Zeichenfolge *xp mit n Hexadezimalstellen

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 .

0
7vujy0f0hy

Die Lösung von ZincX wurde angepasst, um Doppelpunktbegrenzer aufzunehmen:

char buf[] = {0,1,10,11};
int i, size = sizeof(buf) / sizeof(char);
char *buf_str = (char*) malloc(3 * size), *buf_ptr = buf_str;
if (buf_str) {
  for (i = 0; i < size; i++)
    buf_ptr += sprintf(buf_ptr, i < size - 1 ? "%02X:" : "%02X\0", buf[i]);
  printf("%s\n", buf_str);
  free(buf_str);
}
0
Gnubie

Ich füge hier die C++ - Version für alle Interessierten hinzu.

#include <iostream>
#include <iomanip>
inline void print_bytes(char const * buffer, std::size_t count, std::size_t bytes_per_line, std::ostream & out) {
    std::ios::fmtflags flags(out.flags()); // Save flags before manipulation.
    out << std::hex << std::setfill('0');
    out.setf(std::ios::uppercase);
    for (std::size_t i = 0; i != count; ++i) {
        auto current_byte_number = static_cast<unsigned int>(static_cast<unsigned char>(buffer[i]));
        out << std::setw(2) << current_byte_number;
        bool is_end_of_line = (bytes_per_line != 0) && ((i + 1 == count) || ((i + 1) % bytes_per_line == 0));
        out << (is_end_of_line ? '\n' : ' ');
    }
    out.flush();
    out.flags(flags); // Restore original flags.
}

Es wird den Hexdump der Variablen buffer der Länge count an std::ostreamout ausgegeben (Sie können den Standardwert std::cout festlegen). Jede Zeile enthält bytes_per_line Bytes, jedes Byte wird durch zweistellige Hexadezimalzeichen dargestellt. Zwischen den Bytes ist ein Leerzeichen. Am Ende der Zeile oder am Ende des Puffers wird eine neue Zeile gedruckt. Wenn bytes_per_line auf 0 gesetzt ist, wird new_line nicht gedruckt. Versuchen Sie es selbst.

0
WiSaGaN

Wenn Sie die Hex-Werte in einer char *-Zeichenfolge speichern möchten, können Sie snprintf verwenden. Sie müssen Platz für alle gedruckten Zeichen, einschließlich der führenden Nullen und des Doppelpunkts, reservieren.

Erweiterung auf Marks Antwort:

char str_buf* = malloc(3*X + 1);   // X is the number of bytes to be converted

int i;
for (i = 0; i < x; i++)
{
    if (i > 0) snprintf(str_buf, 1, ":");
    snprintf(str_buf, 2, "%02X", num_buf[i]);  // need 2 characters for a single hex value
}
snprintf(str_buf, 2, "\n\0"); // dont forget the NULL byte

Nun wird str_buf den Hex-String enthalten.

0
Mr. Shickadance

Es gibt kein primitives Element für C. In diesem Fall würde ich wahrscheinlich einen ausreichend langen Puffer malloc (oder vielleicht allokieren) und die Eingabe überlaufen. Ich habe es auch mit einer dynamischen String-Bibliothek mit Semantik (aber nicht mit Syntax!) Gesehen, ähnlich wie ostringstream von C++, die eine plausibelere generische Lösung darstellt, die zusätzliche Komplexität jedoch nicht nur für einen einzelnen Fall wert ist.

0
crazyscot

Zur einfachen Verwendung habe ich eine Funktion erstellt, die die Eingabezeichenfolge (Binärdaten) kodiert:

/* Encodes string to hexadecimal string reprsentation
    Allocates a new memory for supplied lpszOut that needs to be deleted after use
    Fills the supplied lpszOut with hexadecimal representation of the input
    */
void StringToHex(unsigned char *szInput, size_t size_szInput, char **lpszOut)
{
    unsigned char *pin = szInput;
    const char *hex = "0123456789ABCDEF";
    size_t outSize = size_szInput * 2 + 2;
    *lpszOut = new char[outSize];
    char *pout = *lpszOut;
    for (; pin < szInput + size_szInput; pout += 2, pin++)
    {
        pout[0] = hex[(*pin >> 4) & 0xF];
        pout[1] = hex[*pin & 0xF];
    }
    pout[0] = 0;
}

Verwendungszweck:

unsigned char input[] = "This is a very long string that I want to encode";
char *szHexEncoded = NULL;
StringToHex(input, strlen((const char *)input), &szHexEncoded);

printf(szHexEncoded);

// The allocated memory needs to be deleted after usage
delete[] szHexEncoded;
0
mbuster

Leicht modifizierte Yannith-Version

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

0
Mikhail Baynov

Basierend auf Yannuths Antwort aber vereinfacht.

Hier ist die Länge von dest[] doppelt so groß wie len, und ihre Zuweisung wird vom Anrufer verwaltet.

void create_hex_string_implied(const unsigned char *src, size_t len, unsigned char *dest)
{
    static const unsigned char table[] = "0123456789abcdef";

    for (; len > 0; --len)
    {
        unsigned char c = *src++;
        *dest++ = table[c >> 4];
        *dest++ = table[c & 0x0f];
    }
}
0
konsolebox

Ich weiß, dass diese Frage bereits eine Antwort hat, aber ich denke, meine Lösung könnte jemandem helfen.

In meinem Fall hatte ich also ein Byte-Array, das den Schlüssel darstellt, und ich musste dieses Byte-Array in ein char-Array mit Hexadezimalwerten konvertieren, um es in einer Zeile auszudrucken. Ich habe meinen Code in eine Funktion wie folgt extrahiert:

char const * keyToStr(uint8_t const *key)
{
    uint8_t offset = 0;
    static char keyStr[2 * KEY_SIZE + 1];

    for (size_t i = 0; i < KEY_SIZE; i++)
    {
        offset += sprintf(keyStr + offset, "%02X", key[i]);
    }
    sprintf(keyStr + offset, "%c", '\0');

    return keyStr;
}

Nun kann ich meine Funktion folgendermaßen verwenden:

Serial.print("Public key: ");
Serial.println(keyToStr(m_publicKey));

Das Serial-Objekt ist Teil der Arduino-Bibliothek und m_publicKey ist Mitglied meiner Klasse mit der folgenden Deklaration uint8_t m_publicKey[32].

0
NutCracker