Matriz hexadecimal a byte en C

¿Hay alguna función C estándar que convierta de cadena hexadecimal a matriz de bytes ?
No quiero escribir mi propia función.

Hasta donde yo sé, no hay una función estándar para hacerlo, pero es fácil de lograr de la siguiente manera:

#include  int main(int argc, char **argv) { const char hexstring[] = "DEadbeef10203040b00b1e50", *pos = hexstring; unsigned char val[12]; /* WARNING: no sanitization or error-checking whatsoever */ for (size_t count = 0; count < sizeof val/sizeof *val; count++) { sscanf(pos, "%2hhx", &val[count]); pos += 2; } printf("0x"); for(size_t count = 0; count < sizeof val/sizeof *val; count++) printf("%02x", val[count]); printf("\n"); return 0; } 

Editar

Como señala Al, en el caso de un número impar de dígitos hexadecimales en la cadena, debe asegurarse de que lo prefija con un 0 inicial. Por ejemplo, la cadena "f00f5" se evaluará como {0xf0, 0x0f, 0x05} erróneamente por el ejemplo anterior, en lugar del correcto {0x0f, 0x00, 0xf5} .

Modificó el ejemplo un poco para abordar el comentario de @MassimoCallegari

Encontré esta pregunta en Google para la misma cosa. No me gusta la idea de llamar a sscanf () o strtol (), ya que parece exagerado. Escribí una función rápida que no valida que el texto sea de hecho la presentación hexadecimal de una secuencia de bytes, pero manejará un número impar de dígitos hexadecimales:

 uint8_t tallymarker_hextobin(const char * str, uint8_t * bytes, size_t blen) { uint8_t pos; uint8_t idx0; uint8_t idx1; // mapping of ASCII characters to hex values const uint8_t hashmap[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // !"#$%&' 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ()*+,-./ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;< =>? 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // @ABCDEFG 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // HIJKLMNO 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // PQRSTUVW 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // XYZ[\]^_ 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // `abcdefg 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // hijklmno 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pqrstuvw 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // xyz{|}~. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ........ }; bzero(bytes, blen); for (pos = 0; ((pos < (blen*2)) && (pos < strlen(str))); pos += 2) { idx0 = (uint8_t)str[pos+0]; idx1 = (uint8_t)str[pos+1]; bytes[pos/2] = (uint8_t)(hashmap[idx0] << 4) | hashmap[idx1]; }; return(0); } 

Para cadenas cortas, strtol , strtoll y strtoimax funcionarán bien (tenga en cuenta que el tercer argumento es la base para usar en el procesamiento de la cadena … strtoimax en 16). Si su entrada es más larga que number-of-bits-in-the-longest-integer-type/4 entonces necesitará uno de los métodos más flexibles sugeridos por otras respuestas.

Además de las excelentes respuestas anteriores, pensé que escribiría una función C que no utiliza ninguna biblioteca y que tiene algunos resguardos contra cadenas malas.

 uint8_t* datahex(char* string) { if(string == NULL) return NULL; size_t slength = strlen(string); if((slength % 2) != 0) // must be even return NULL; size_t dlength = slength / 2; uint8_t* data = malloc(dlength); memset(data, 0, dlength); size_t index = 0; while (index < slength) { char c = string[index]; int value = 0; if(c >= '0' && c < = '9') value = (c - '0'); else if (c >= 'A' && c < = 'F') value = (10 + (c - 'A')); else if (c >= 'a' && c < = 'f') value = (10 + (c - 'a')); else { free(data); return NULL; } data[(index/2)] += value << (((index + 1) % 2) * 4); index++; } return data; } 

Explicación:

a. índice / 2 | La división entre enteros redondeará el valor, entonces 0/2 = 0, 1/2 = 0, 2/2 = 1, 3/2 = 0 etc. Por lo tanto, por cada 2 caracteres de cadena agregamos el valor a 1 byte de datos .

segundo. (índice + 1)% 2 | Queremos que los números impares resulten en 1 e incluso en 0, ya que el primer dígito de una cadena hexagonal es el más significativo y debe multiplicarse por 16. Entonces, para el índice 0 => 0 + 1% 2 = 1, índice 1 => 1 + 1% 2 = 0 etc.

do. < < 4 | Shift por 4 se está multiplicando por 16. Ejemplo: b00000001 < < 4 = b00010000

Por alguna modificación del código de user411313, me sigue el siguiente:

 #include  #include  #include  int main () { char *hexstring = "deadbeef10203040b00b1e50"; int i; unsigned int bytearray[12]; uint8_t str_len = strlen(hexstring); for (i = 0; i < (str_len / 2); i++) { sscanf(hexstring + 2*i, "%02x", &bytearray[i]); printf("bytearray %d: %02x\n", i, bytearray[i]); } return 0; } 

Una versión desarrollada de la publicación de Michael Foukarakis (ya que aún no tengo la “reputación” para agregar un comentario a esa publicación):

 #include  #include  void print(unsigned char *byte_array, int byte_array_size) { int i = 0; printf("0x"); for(; i < byte_array_size; i++) { printf("%02x", byte_array[i]); } printf("\n"); } int convert(const char *hex_str, unsigned char *byte_array, int byte_array_max) { int hex_str_len = strlen(hex_str); int i = 0, j = 0; // The output array size is half the hex_str length (rounded up) int byte_array_size = (hex_str_len+1)/2; if (byte_array_size > byte_array_max) { // Too big for the output array return -1; } if (hex_str_len % 2 == 1) { // hex_str is an odd length, so assume an implicit "0" prefix if (sscanf(&(hex_str[0]), "%1hhx", &(byte_array[0])) != 1) { return -1; } i = j = 1; } for (; i < hex_str_len; i+=2, j++) { if (sscanf(&(hex_str[i]), "%2hhx", &(byte_array[j])) != 1) { return -1; } } return byte_array_size; } void main() { char *examples[] = { "", "5", "D", "5D", "5Df", "deadbeef10203040b00b1e50", "02invalid55" }; unsigned char byte_array[128]; int i = 0; for (; i < sizeof(examples)/sizeof(char *); i++) { int size = convert(examples[i], byte_array, 128); if (size < 0) { printf("Failed to convert '%s'\n", examples[i]); } else if (size == 0) { printf("Nothing to convert for '%s'\n", examples[i]); } else { print(byte_array, size); } } } 

No. Pero es relativamente trivial lograr usar sscanf en un bucle.

 char *hexstring = "deadbeef10203040b00b1e50", *pos = hexstring; unsigned char val[12]; while( *pos ) { if( !((pos-hexstring)&1) ) sscanf(pos,"%02x",&val[(pos-hexstring)>>1]); ++pos; } 

¡sizeof (val) / sizeof (val [0]) es redundante!

  In main() { printf("enter string :\n"); fgets(buf, 200, stdin); unsigned char str_len = strlen(buf); k=0; unsigned char bytearray[100]; for(j=0;j64) temp=10+(temp-65); else temp=temp-48; fin=(temp< <4)&0xf0; temp = toupper(*(val+1)); if(temp>64) temp=10+(temp-65); else temp=temp-48; fin=fin|(temp & 0x0f); return fin; } 

Esta es una función modificada de una pregunta similar, modificada según la sugerencia de https://stackoverflow.com/a/18267932/700597 .

Esta función convertirá una cadena hexadecimal – NO precedida de “0x” – con un número par de caracteres al número de bytes especificado. Devolverá -1 si encuentra un carácter inválido, o si la cadena hexagonal tiene una longitud impar, y 0 si tiene éxito.

 //convert hexstring to len bytes of data //returns 0 on success, -1 on error //data is a buffer of at least len bytes //hexstring is upper or lower case hexadecimal, NOT prepended with "0x" int hex2data(unsigned char *data, const unsigned char *hexstring, unsigned int len) { unsigned const char *pos = hexstring; char *endptr; size_t count = 0; if ((hexstring[0] == '\0') || (strlen(hexstring) % 2)) { //hexstring contains no data //or hexstring has an odd length return -1; } for(count = 0; count < len; count++) { char buf[5] = {'0', 'x', pos[0], pos[1], 0}; data[count] = strtol(buf, &endptr, 0); pos += 2 * sizeof(char); if (endptr[0] != '\0') { //non-hexadecimal character encountered return -1; } } return 0; } 

Aquí HexToBin y BinToHex son relativamente limpios y legibles. (Nota: originalmente se devolvieron los códigos de error enum a través de un sistema de registro de errores no simple -1 o -2).

 typedef unsigned char ByteData; ByteData HexChar (char c) { if ('0' < = c && c <= '9') return (ByteData)(c - '0'); if ('A' <= c && c <= 'F') return (ByteData)(c - 'A' + 10); if ('a' <= c && c <= 'f') return (ByteData)(c - 'a' + 10); return (ByteData)(-1); } ssize_t HexToBin (const char* s, ByteData * buff, ssize_t length) { ssize_t result = 0; if (!s || !buff || length <= 0) return -2; while (*s) { ByteData nib1 = HexChar(*s++); if ((signed)nib1 < 0) return -3; ByteData nib2 = HexChar(*s++); if ((signed)nib2 < 0) return -4; ByteData bin = (nib1 << 4) + nib2; if (length-- <= 0) return -5; *buff++ = bin; ++result; } return result; } void BinToHex (const ByteData * buff, ssize_t length, char * output, ssize_t outLength) { char binHex[] = "0123456789ABCDEF"; if (!output || outLength < 4) return (void)(-6); *output = '\0'; if (!buff || length <= 0 || outLength <= 2 * length) { memcpy(output, "ERR", 4); return (void)(-7); } for (; length > 0; --length, outLength -= 2) { ByteData byte = *buff++; *output++ = binHex[(byte >> 4) & 0x0F]; *output++ = binHex[byte & 0x0F]; } if (outLength-- < = 0) return (void)(-8); *output++ = '\0'; } 

hextools.h

 #ifndef HEX_TOOLS_H #define HEX_TOOLS_H char *bin2hex(unsigned char*, int); unsigned char *hex2bin(const char*); #endif // HEX_TOOLS_H 

hextools.c

 #include  char *bin2hex(unsigned char *p, int len) { char *hex = malloc(((2*len) + 1)); char *r = hex; while(len && p) { (*r) = ((*p) & 0xF0) >> 4; (*r) = ((*r) < = 9 ? '0' + (*r) : 'A' - 10 + (*r)); r++; (*r) = ((*p) & 0x0F); (*r) = ((*r) <= 9 ? '0' + (*r) : 'A' - 10 + (*r)); r++; p++; len--; } *r = '\0'; return hex; } unsigned char *hex2bin(const char *str) { int len, h; unsigned char *result, *err, *p, c; err = malloc(1); *err = 0; if (!str) return err; if (!*str) return err; len = 0; p = (unsigned char*) str; while (*p++) len++; result = malloc((len/2)+1); h = !(len%2) * 4; p = result; *p = 0; c = *str; while(c) { if(('0' <= c) && (c <= '9')) *p += (c - '0') << h; else if(('A' <= c) && (c <= 'F')) *p += (c - 'A' + 10) << h; else if(('a' <= c) && (c <= 'f')) *p += (c - 'a' + 10) << h; else return err; str++; c = *str; if (h) h = 0; else { h = 4; p++; *p = 0; } } return result; } 

C Principal

 #include  #include "hextools.h" int main(void) { unsigned char s[] = { 0xa0, 0xf9, 0xc3, 0xde, 0x44 }; char *hex = bin2hex(s, sizeof s); puts(hex); unsigned char *bin; bin = hex2bin(hex); puts(bin2hex(bin, 5)); size_t k; for(k=0; k<5; k++) printf("%02X", bin[k]); putchar('\n'); return 0; } 

Pruebe el siguiente código:

 static unsigned char ascii2byte(char *val) { unsigned char temp = *val; if(temp > 0x60) temp -= 39; // convert chars af temp -= 48; // convert chars 0-9 temp *= 16; temp += *(val+1); if(*(val+1) > 0x60) temp -= 39; // convert chars af temp -= 48; // convert chars 0-9 return temp; } 

Aquí está mi versión:

 /* Convert a hex char digit to its integer value. */ int hexDigitToInt(char digit) { digit = tolower(digit); if ('0' < = digit && digit <= '9') //if it's decimal return (int)(digit - '0'); else if ('a' <= digit && digit <= 'f') //if it's abcdef return (int)(digit - ('a' - 10)); else return -1; //value not in [0-9][af] range } /* Decode a hex string. */ char *decodeHexString(const char *hexStr) { char* decoded = malloc(strlen(hexStr)/2+1); char* hexStrPtr = (char *)hexStr; char* decodedPtr = decoded; while (*hexStrPtr != '\0') { /* Step through hexStr, two chars at a time. */ *decodedPtr = 16 * hexDigitToInt(*hexStrPtr) + hexDigitToInt(*(hexStrPtr+1)); hexStrPtr += 2; decodedPtr++; } *decodedPtr = '\0'; /* final null char */ return decoded; } 

¿Podría ser más simple?

 uint8_t hex(char ch) { uint8_t r = (ch > 57) ? (ch - 55) : (ch - 48); return r & 0x0F; } int to_byte_array(const char *in, size_t in_size, uint8_t *out) { int count = 0; if (in_size % 2) { while (*in && out) { *out = hex(*in++); if (!*in) return count; *out = (*out < < 4) | hex(*in++); *out++; count++; } return count; } else { while (*in && out) { *out++ = (hex(*in++) << 4) | hex(*in++); count++; } return count; } } int main() { char hex_in[] = "deadbeef10203040b00b1e50"; uint8_t out[32]; int res = to_byte_array(hex_in, sizeof(hex_in) - 1, out); for (size_t i = 0; i < res; i++) printf("%02x ", out[i]); printf("\n"); system("pause"); return 0; }