Hexadecimal string to byte array in C

Is there any standard C function that converts from a hexadecimal string to an array of bytes ?
I do not want to write my own function.

+54
c string
Aug 04 '10 at 18:40
source share
18 answers

As far as I know, the standard function for this does not exist, but it is easy to achieve it as follows:

#include <stdio.h> 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; } 



Edit

As Al pointed out, in the case of an odd number of hexadecimal digits in a string, you must make sure that you prefix it with the beginning 0. For example, the string "f00f5" will evaluate to {0xf0, 0x0f, 0x05} incorrectly in the above example instead of the correct {0x0f, 0x00, 0xf5} .

Slightly modified example to respond to @MassimoCallegari comment

+63
Aug 04 '10 at 7:40
source share

I found this question on Google for the same. I don't like the idea of โ€‹โ€‹calling sscanf () or strtol () as it feels redundant. I wrote a quick function that does not confirm that the text is really a hexadecimal representation of the byte stream, but will handle an odd number of hexadecimal digits:

 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); } 
+12
May 27 '14 at 20:35
source share

For short strings, strtoll , strtoll and strtoimax will work very well (note that the third argument is the base for use in processing the string ... set it to 16). If your input is longer than number-of-bits-in-the-longest-integer-type/4 , then you will need one of the more flexible methods suggested by the other answers.

+8
Oct 24 2018-11-21T00:
source share

Besides the excellent answers above, I would like to write a C function that does not use libraries and protects against bad strings.

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

Explanation:

but. index / 2 | The division between integers rounds the value, so 0/2 = 0, 1/2 = 0, 2/2 = 1, 3/2 = 1, 4/2 = 2, 5/2 = 2, etc. So, for every 2 string characters, we add a value of 1 byte of data.

b. (index + 1)% 2 | We want the odd numbers to lead to 1 and even to 0, since the first digit of the hexadecimal string is the most significant and should be multiplied by 16. Therefore, for the index 0 => 0 + 1% 2 = 1, the index 1 => 1 + 1 % 2 = 0 etc.

from. & L; & l; 4 | A shift of 4 is a multiplication by 16. Example: b00000001 & lt; & lt; 4 = b00010000

+7
Feb 17 '16 at 9:05
source share

For some modification of the user411313 code form, the following works for me:

 #include <stdio.h> #include <stdint.h> #include <string.h> 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; } 
+3
Nov 02
source share

Formed version of the message of Michael Fukarakis (since I have no "reputation" to add a comment to this post):

 #include <stdio.h> #include <string.h> 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); } } } 
+3
Jun 23 '15 at 15:33
source share

Here, HexToBin and BinToHex are relatively clean and readable. (Note: Enum error codes were initially returned through the error logging system is not simple -1 or -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'; } 
+2
Jan 25 '17 at 1:21 on
source share

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 <stdlib.h> 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 <stdio.h> #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; } 
+2
Oct 01 '17 at 17:08 on
source share

The following is a solution that I wrote for the following reasons:

 void hex2bin(const char* in, size_t len, unsigned char* out) { static const unsigned char TBL[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 58, 59, 60, 61, 62, 63, 64, 10, 11, 12, 13, 14, 15, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 10, 11, 12, 13, 14, 15 }; static const unsigned char *LOOKUP = TBL - 48; const char* end = in + len; while(in < end) *(out++) = LOOKUP[*(in++)] << 4 | LOOKUP[*(in++)]; } 

Example:

 unsigned char seckey[32]; hex2bin("351aaaec0070d13d350afae2bc43b68c7e590268889869dde489f2f7988f3fee", 64, seckey); /* seckey = { 53, 26, 170, 236, 0, 112, 209, 61, 53, 10, 250, 226, 188, 67, 182, 140, 126, 89, 2, 104, 136, 152, 105, 221, 228, 137, 242, 247, 152, 143, 63, 238 }; */ 

If you do not need to support lowercase letters:

 static const unsigned char TBL[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 58, 59, 60, 61, 62, 63, 64, 10, 11, 12, 13, 14, 15 }; 
+2
Dec 02 '18 at 10:24
source share
 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]) is redundant!

+1
Aug 05 '10 at
source share
  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;j<str_len-1;j=j+2) { bytearray[k++]=converttohex(&buffer[j]); printf(" %02X",bytearray[k-1]); } } Use this int converttohex(char * val) { unsigned char temp = toupper(*val); unsigned char fin=0; if(temp>64) 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; } 
+1
Apr 03 '13 at 10:17
source share

This is a modified function from a similar question, modified according to the suggestion of https://stackoverflow.com/a/212625/2/ .

This function converts a hexadecimal string - NOT added with "0x" - with an even number of characters to the number of bytes specified. It will return -1 if it encounters an invalid character, or if the hexadecimal string has an odd length and 0 if successful.

 //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; } 
+1
Feb 02 '14 at 13:05
source share

No. But this is relatively trivial to achieve with sscanf in a loop.

0
Aug 04 '10 at 18:48
source share

Try using the following code:

 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; } 
0
03 Mar. '13 at 13:14
source share

Here is my version:

 /* Convert a hex char digit to its integer value. */ int hexDigitToInt(char digit) { digit = tolower(digit); if ('0' <= digit && digit <= '9') //if it decimal return (int)(digit - '0'); else if ('a' <= digit && digit <= 'f') //if it 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; } 
0
Jan 30 '16 at 5:30
source share

Maybe it's easier?

 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; } 
0
Apr 29 '18 at 10:46
source share

The best way I know:

 int hex2bin_by_zibri(char *source_str, char *dest_buffer) { char *line = source_str; char *data = line; int offset; int read_byte; int data_len = 0; while (sscanf(data, " %02x%n", &read_byte, &offset) == 1) { dest_buffer[data_len++] = read_byte; data += offset; } return data_len; } 

The function returns the number of converted bytes stored in dest_buffer. The input string may contain spaces and letters in mixed case.

"01 02 03 04 ab Cd eF garbage AB"

translates to dest_buffer containing 01 02 03 04 ab cd ef

as well as "01020304abCdeFgarbageAB"

translates as before.

Parsing stops at the first "error".

0
May 21 '19 at 23:02
source share

My version using bitwise operations:

 char hex_to_byte(char hex[2]) { char result = 0; if (hex[1] & 0b01000000) result |= hex[1] + 0b1001; //af else result |= hex[1]; //0-9 result &= 0b00001111; if (hex[0] & 0b01000000) //af result |= (hex[0] << 4) + 0b1001; else //0-9 result |= hex[0] << 4; return result; } 

Test:

 #include <unistd.h> char test[26] = "48656c6c6f20776f726c64210a"; //Hello world!\n char result[13]; for (int i = 0; i < 13; i++) result[i] = hex_to_byte(&test[i*2]); write(1, result, 13); 

Of:

Hello world!

0
Jun 24 '19 at 10:06 on
source share



All Articles