[c] Hexadecimal string to byte array in C

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

This question is related to c string

The answer is


No. But it's relatively trivial to achieve using sscanf in a loop.


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

Here's 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'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][a-f] 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;
}

Apart from the excellent answers above I though I would write a C function that does not use any libraries and has some guards 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:

a. index / 2 | Division between integers will round down 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 the value to 1 data byte.

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

c. << 4 | Shift by 4 is multiplying by 16. example: b00000001 << 4 = b00010000


A fleshed out version of Michael Foukarakis post (since I don't have the "reputation" to add a comment to that post yet):

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

Two short routines to parse a byte or a word, using strchr().

_x000D_
_x000D_
// HexConverter.h_x000D_
#ifndef HEXCONVERTER_H_x000D_
#define HEXCONVERTER_H_x000D_
unsigned int hexToByte (const char *hexString);_x000D_
unsigned int hexToWord (const char *hexString);_x000D_
#endif_x000D_
_x000D_
_x000D_
// HexConverter.c_x000D_
#include <string.h> // for strchr()_x000D_
#include <ctype.h>  // for toupper()_x000D_
_x000D_
unsigned int hexToByte (const char *hexString)_x000D_
{_x000D_
    unsigned int value;_x000D_
    const char *hexDigits = "0123456789ABCDEF";_x000D_
_x000D_
    value = 0;_x000D_
    if (hexString != NULL)_x000D_
    {_x000D_
        char *ptr;_x000D_
_x000D_
        ptr = strchr (hexDigits, toupper(hexString[0]));_x000D_
        if (ptr != NULL)_x000D_
        {_x000D_
            value = (ptr - hexDigits) << 4;_x000D_
_x000D_
            ptr = strchr (hexDigits, toupper(hexString[1]));_x000D_
            if (ptr != NULL)_x000D_
            {_x000D_
                value = value | (ptr - hexDigits);_x000D_
            }_x000D_
        }_x000D_
    }_x000D_
_x000D_
    return value;_x000D_
}_x000D_
_x000D_
unsigned int hexToWord (const char *hexString)_x000D_
{_x000D_
    unsigned int value;_x000D_
_x000D_
    value = 0;_x000D_
    if (hexString != NULL)_x000D_
    {_x000D_
        value = (hexToByte (&hexString[0]) << 8) |_x000D_
                (hexToByte (&hexString[2]));_x000D_
    }_x000D_
_x000D_
    return value;_x000D_
}_x000D_
_x000D_
_x000D_
// HexConverterTest.c_x000D_
#include <stdio.h>_x000D_
_x000D_
#include "HexConverter.h"_x000D_
_x000D_
int main (int argc, char **argv)_x000D_
{_x000D_
    (void)argc; // not used_x000D_
    (void)argv; // not used_x000D_
_x000D_
    unsigned int value;_x000D_
    char *hexString;_x000D_
_x000D_
    hexString = "2a";_x000D_
    value = hexToByte (hexString);_x000D_
    printf ("%s == %x (%u)\n", hexString, value, value);_x000D_
_x000D_
    hexString = "1234";_x000D_
    value = hexToWord (hexString);_x000D_
    printf ("%s == %x (%u)\n", hexString, value, value);_x000D_
_x000D_
    hexString = "0102030405060708090a10ff";_x000D_
    printf ("Hex String: %s\n", hexString);_x000D_
    for (unsigned int idx = 0; idx < strlen(hexString); idx += 2)_x000D_
    {_x000D_
        value = hexToByte (&hexString[idx]);_x000D_
        printf ("%c%c == %x (%u)\n", hexString[idx], hexString[idx+1],_x000D_
                value, value);_x000D_
    }_x000D_
_x000D_
    return EXIT_SUCCESS;_x000D_
}
_x000D_
_x000D_
_x000D_


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 saved in dest_buffer. The input string can contain spaces and mixed case letters.

"01 02 03 04 ab Cd eF garbage AB"

translates to dest_buffer containing 01 02 03 04 ab cd ef

and also "01020304abCdeFgarbageAB"

translates as before.

Parsing stops at the first "error".


This is a modified function from a similar question, modified as per the suggestion of https://stackoverflow.com/a/18267932/700597.

This function will convert a hexadecimal string - NOT prepended 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 hex string has an odd length, and 0 on success.

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

By some modification form user411313's code, 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;
}

Try the following code:

static unsigned char ascii2byte(char *val)
{
    unsigned char temp = *val;

    if(temp > 0x60) temp -= 39;  // convert chars a-f
    temp -= 48;  // convert chars 0-9
    temp *= 16;

    temp += *(val+1);
    if(*(val+1) > 0x60) temp -= 39;  // convert chars a-f
    temp -= 48;  // convert chars 0-9   

    return temp;

}

I found this question by Googling for the same thing. I don't like the idea of calling sscanf() or strtol() since it feels like overkill. I wrote a quick function which does not validate that the text is indeed the hexadecimal presentation of a byte stream, but will handle odd number of hex 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);
}

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

Here is HexToBin and BinToHex relatively clean and readable. (Note originally there were returned enum error codes through an error logging system not a 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';
}

Here is a solution to deal with files, which may be used more frequently...

int convert(char *infile, char *outfile) {

char *source = NULL;
FILE *fp = fopen(infile, "r");
long bufsize;
if (fp != NULL) {
    /* Go to the end of the file. */
    if (fseek(fp, 0L, SEEK_END) == 0) {
        /* Get the size of the file. */
        bufsize = ftell(fp);
        if (bufsize == -1) { /* Error */ }

        /* Allocate our buffer to that size. */
        source = malloc(sizeof(char) * (bufsize + 1));

        /* Go back to the start of the file. */
        if (fseek(fp, 0L, SEEK_SET) != 0) { /* Error */ }

        /* Read the entire file into memory. */
        size_t newLen = fread(source, sizeof(char), bufsize, fp);
        if ( ferror( fp ) != 0 ) {
            fputs("Error reading file", stderr);
        } else {
            source[newLen++] = '\0'; /* Just to be safe. */
        }
    }
    fclose(fp);
}
     
int sourceLen = bufsize - 1;
int destLen = sourceLen/2;
unsigned char* dest = malloc(destLen);
short i;  
unsigned char highByte, lowByte;  
      
for (i = 0; i < sourceLen; i += 2)  
{  
        highByte = toupper(source[i]);  
        lowByte  = toupper(source[i + 1]);  
  
        if (highByte > 0x39)  
            highByte -= 0x37;  
        else  
            highByte -= 0x30;  
  
        if (lowByte > 0x39)  
            lowByte -= 0x37;  
        else  
            lowByte -= 0x30;  
  
        dest[i / 2] = (highByte << 4) | lowByte;  
}  
        

FILE *fop = fopen(outfile, "w");
if (fop == NULL) return 1;
fwrite(dest, 1, destLen, fop);
fclose(fop);
free(source);
free(dest);
return 0;
}  

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!


Following is the solution I wrote up for performance 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 don't need to support lowercase:

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

For short strings, strtol, strtoll, and strtoimax will work just fine (note that the third argument is the base to 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'll need one of the more flexible methods suggested by other answers.


Could it be simpler?!

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