Stringa esadecimale su array di byte in C

Esiste una funzione C standard che converta da stringa esadecimale in array di byte ?
Non voglio scrivere la mia funzione.

Per quanto ne so, non esiste una funzione standard per farlo, ma è semplice da ottenere nel modo seguente:

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

modificare

Come sottolineato da Al, in caso di un numero dispari di cifre esadecimali nella stringa, devi assicurarti di prefissarlo con uno "f00f5" iniziale. Ad esempio, la stringa "f00f5" sarà valutata come {0xf0, 0x0f, 0x05} erroneamente dall'esempio precedente, invece del corretto {0x0f, 0x00, 0xf5} .

Modificato un po 'l'esempio per indirizzare il commento di @MassimoCallegari

Ho trovato questa domanda da Googling per la stessa cosa. Non mi piace l’idea di chiamare sscanf () o strtol () poiché sembra eccessivo. Ho scritto una funzione rapida che non convalida il fatto che il testo sia effettivamente la presentazione esadecimale di un stream di byte, ma gestirà un numero dispari di cifre esadecimali:

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

Per le stringhe brevi, strtol , strtoll e strtoimax funzioneranno strtoimax (si noti che il terzo argomento è la base da utilizzare per elaborare la stringa … impostarla su 16). Se il tuo input è più lungo di number-of-bits-in-the-longest-integer-type/4 allora avrai bisogno di uno dei metodi più flessibili suggeriti da altre risposte.

A parte le eccellenti risposte di cui sopra, penso che scriverei una funzione C che non usa alcuna libreria e ha delle guardie contro le stringhe cattive.

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

Spiegazione:

un. indice / 2 | La divisione tra numeri interi arrotonda il valore, quindi 0/2 = 0, 1/2 = 0, 2/2 = 1, 3/2 = 0 ecc. Quindi, per ogni 2 caratteri di stringa aggiungiamo il valore a 1 byte di dati .

b. (indice + 1)% 2 | Vogliamo numeri dispari da risultare a 1 e anche a 0 poiché la prima cifra di una stringa esadecimale è la più significativa e deve essere moltiplicata per 16. quindi per l'indice 0 => 0 + 1% 2 = 1, indice 1 => 1 + 1% 2 = 0 ecc.

c. < < 4 | Il cambio di 4 è moltiplicato per 16. Esempio: b00000001 < < 4 = b00010000

Con qualche modifica del codice di user411313, i seguenti lavori per me:

 #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 versione aggiornata del post di Michael Foukarakis (dato che non ho ancora la “reputazione” di aggiungere un commento a quel post):

 #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. Ma è relativamente banale ottenere usando sscanf in un ciclo.

 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]) è ridondante!

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

Questa è una funzione modificata da una domanda simile, modificata secondo il suggerimento di https://stackoverflow.com/a/18267932/700597 .

Questa funzione convertirà una stringa esadecimale – NON anteponendo “0x” – con un numero pari di caratteri al numero di byte specificato. Restituirà -1 se incontra un carattere non valido, o se la stringa esadecimale ha una lunghezza dispari e 0 in caso di successo.

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

Qui HexToBin e BinToHex sono relativamente puliti e leggibili. (Nota originariamente ci sono stati restituiti i codici di errore enum attraverso un sistema di registrazione degli errori non un semplice -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; } 

main.c

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

Prova il seguente codice:

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

Ecco la mia versione:

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

Potrebbe essere più semplice ?!

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