¿Cómo se convierte una matriz de bytes a una cadena hexadecimal en C?

Yo tengo:

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

Quiero convertir la matriz de bytes en una cadena de manera que pueda imprimir la cadena usando printf:

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

y get (los dos puntos no son necesarios):

 "00:01:0A:0B" 

Cualquier ayuda sería muy apreciada.

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

para una forma más genérica:

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

para concatenar a una cadena, hay algunas formas en que puede hacer esto … probablemente mantendría un puntero al final de la cadena y usaría sprintf. también debe realizar un seguimiento del tamaño de la matriz para asegurarse de que no sea más grande que el espacio asignado:

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

Para completitud, también puede hacerlo fácilmente sin llamar a ninguna función de biblioteca pesada (sin snprintf, sin strcat, ni siquiera memcpy). Puede ser útil, por ejemplo, si está progtwigndo algún microcontrolador o kernel de sistema operativo donde libc no está disponible.

No hay nada realmente lujoso que pueda encontrar un código similar si busca en Google. Realmente no es mucho más complicado que llamar snprintf y mucho más rápido.

 #include  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); } 

Aquí hay otra versión un poco más corta. Simplemente evita la variable de índice intermedio i y duplica el código de caso de la última (pero el carácter de terminación se escribe dos veces).

 #include  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); } 

A continuación hay otra versión para responder a un comentario diciendo que usé un “truco” para conocer el tamaño del buffer de entrada. En realidad, no es un truco, sino un conocimiento de entrada necesario (es necesario conocer el tamaño de los datos que está convirtiendo). Lo hice más claro extrayendo el código de conversión a una función separada. También agregué el código de verificación de límite para el buffer de destino, que no es realmente necesario si sabemos lo que estamos haciendo.

 #include  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); } 

Aquí hay un método que es mucho más rápido:

 #include  #include  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 } 

Ya existen respuestas similares, agregué esta para explicar cómo funciona exactamente la siguiente línea de código:

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

Es muy complicado y no es fácil de entender, puse la explicación en los comentarios a continuación:

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

Solo quería agregar lo siguiente, incluso si está un poco fuera del tema (no C estándar), pero me encuentro buscándolo a menudo y tropezando con esta pregunta entre los primeros resultados de búsqueda. La función de impresión de kernel de Linux, printk , también tiene especificadores de formato para generar contenido de matriz / memoria “directamente” a través de un especificador de formato singular:

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(). 

… sin embargo, estos especificadores de formato no parecen existir para el formato estándar, espacio-usuario (s)printf .

Esta función es adecuada cuando el usuario / interlocutor quiere que la cadena hexadecimal se coloque en una matriz / búfer de caracteres. Con la cadena hexadecimal en un búfer de caracteres, el usuario / llamante puede usar su propia macro / función para mostrar o registrar en cualquier lugar que desee (por ejemplo, en un archivo). Esta función también permite que el que llama controle el número de bytes (hexadecimales) para poner en cada línea.

 /** * @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)); } 

Ejemplo: Código

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

SALIDA

 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 

No hay nada primitivo para esto en C. Probablemente malloc (o tal vez alloca) un buffer lo suficientemente largo y loop sobre la entrada. También lo he visto hecho con una biblioteca de cadenas dinámica con semántica (¡pero sin syntax!) Similar al ostringstream C ++, que es una solución plausiblemente más genérica pero puede no valer la pena la complejidad adicional solo para un solo caso.

Si desea almacenar los valores hexadecimales en una cadena char * , puede usar snprintf . Debe asignar espacio para todos los caracteres impresos, incluidos los ceros iniciales y los dos puntos.

Ampliando la respuesta de Mark:

 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 

Entonces ahora str_buf contendrá la cadena hexadecimal.

La solución de ZincX adaptada para incluir delimitadores de colon:

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

Esta es una forma de realizar la conversión:

 #include #include #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); } 

Agregaré la versión de C ++ aquí para cualquier persona que esté interesada.

 #include  #include  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(static_cast(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. } 

Imprimirá el hexdump del buffer del count de longitud a std::ostream out (puede hacer que sea predeterminado a std::cout ). Cada línea contendrá bytes bytes por línea, cada byte se representa utilizando mayúsculas hexagonales de dos dígitos. Habrá un espacio entre bytes. Y al final de la línea o al final del buffer, imprimirá una nueva línea. Si bytes_per_line se establece en 0, no imprimirá new_line. Pruébelo usted mismo.

Para un uso simple hice una función que codifica la cadena de entrada (datos binarios):

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

Uso:

 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; 

Versión Yannith ligeramente modificada. Es solo que me gusta tenerlo como un valor de retorno

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

Basado en la respuesta de Yannuth pero simplificado.

Aquí, la longitud de dest[] está implicada para ser dos veces mayor, y su asignación es administrada por la persona que llama.

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

¡Qué soluciones complejas!
Malloc y sprints y moldes oh mi. (Cita OZ)
y ni un solo rem en ninguna parte. Gosh

¿Qué tal algo como esto?

 main() { // the value int value = 16; // create a string array with a '\0' ending ie. 0,0,0 char hex[]= {0,0,'\0'}; char *hex_p=hex; //a working variable int TEMP_int=0; // get me how many 16s are in this code TEMP_int=value/16; // load the first character up with // 48+0 gives you ascii 0, 55+10 gives you ascii A if (TEMP_int<10) {*hex_p=48+TEMP_int;} else {*hex_p=55+TEMP_int;} // move that pointer to the next (less significant byte)
hex_p++; // get me the remainder after I have divied by 16 TEMP_int=value%16; // 48+0 gives you ascii 0, 55+10 gives you ascii A if (TEMP_int<10) {*hex_p=48+TEMP_int;} else {*hex_p=55+TEMP_int;} // print the result printf("%i , 0x%s",value,hex); }