68

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

Andrejs Cainikovs
  • 27,428
  • 2
  • 75
  • 95
Szere Dyeri
  • 14,916
  • 11
  • 39
  • 42
  • 1
    Do you mean a string, which contains characters that represent a hexidecimal number? – Nate Aug 04 '10 at 18:49
  • 1
    Yes, I have user input string such as "abCD12ff34" with any length >= 0 and I want to convert it to an array of bytes like 0xaa, 0xcd, 0x12 etc – Szere Dyeri Aug 04 '10 at 18:57

22 Answers22

79

As far as I know, there's no standard function to do so, but it's simple to achieve in the following manner:

#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 case of an odd number of hex digits in the string, you have to make sure you prefix it with a starting 0. For example, the string "f00f5" will be evaluated as {0xf0, 0x0f, 0x05} erroneously by the above example, instead of the proper {0x0f, 0x00, 0xf5}.

Amended the example a little bit to address the comment from @MassimoCallegari

Michael Foukarakis
  • 39,737
  • 6
  • 87
  • 123
  • 6
    This is a good approach, but be aware that it will give an incorrect result if there is an odd number of digits in the hex string (the implicit zero will prefix the last digit, not the first, so "5ab5c" will be printed as 0x5ab50c rather than 0x05ab5c). – DrAl Aug 05 '10 at 07:02
  • Doesn't endianness matter here? – Geremia Oct 25 '15 at 03:08
  • 1
    @Geremia nope. Endianness only comes into play when you're reinterpreting byte arrays as integer types or vice-versa. http://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html ED: unless you're weird and write your hex literal in little-endian order. Either way, this takes each pair of digits and turns them into a single byte; the resulting endianness is the same as the original. – josaphatv Dec 03 '15 at 23:35
  • Does this work if the source string is uppercase ? E.g. "DEADBEEF..." – Massimo Callegari Nov 30 '17 at 11:01
  • 1
    @MassimoCallegari, yes, because the `x` conversion specifier for scanf works on strings like `strtoul`, which expects input as defined in C11 6.4.4.1. – Michael Foukarakis Nov 30 '17 at 11:30
  • check my approach.. mine allows spaces as separators and mixed case letters: https://stackoverflow.com/a/56247335/236062 – Zibri May 22 '19 at 07:19
21

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);
}
David M. Syzdek
  • 15,360
  • 6
  • 30
  • 40
  • 7
    You can reduce the mapsize to 32 bytes by just adding 4 logic instructions, which is a good tradeoff: `idx0 = (uint8_t)str[pos+0] & 0x1F ^ 0x10;` and the same for **idx1**. then you can remove all bytes before the `01234567` row and all bytes after the `HIJKLMNO` row – 5andr0 Nov 10 '15 at 21:50
  • @5andr0, Posted as a gist: https://gist.github.com/vi/dd3b5569af8a26b97c8e20ae06e804cb . Not checked although. – Vi. Aug 25 '17 at 15:11
  • 2
    `sscanf` and `strtol` are overkill but an unnecessary 32-line hex table isn't? – Jakob Jan 18 '18 at 19:50
  • 1
    `for (pos = 0; ((pos < (blen*2)) && (pos < strlen(str))); pos += 2)` this line will fail for strings longer then 255 because pos is `uint8_t` therefore the loop condition will always evaluate to true – pbn Jan 25 '18 at 14:28
  • It can be only 71 bytes if you force uppercase, and if you want to include lowercase, only 103 bytes are actually needed. 103 bytes of stack to hold a map that could be used hundreds of millions of times is pretty savvy to me, however, adding extra instructions to an iteration that could be called hundreds of millions of times in order to reduce that map's size is, imo, not very sensible. – flcoder Dec 02 '18 at 09:29
  • 3
    @zibri Running at 1,000,000 iterations, my code averaged 0.011 seconds to complete, your code averaged 0.113 seconds. My comment about overkill was in reference to runtime efficiency. Looking back over the comments, there are few good suggestions on how to improve my code including checking for the pos variable for looping. As a side note, you should always include bounds checks on when writing output functions since your function could potentially lead to a buffer overrun if the input hex is longer than the output buffer. – David M. Syzdek Jun 04 '19 at 01:40
  • @DavidM.Syzdek I know.. I was just joking... I also sometimes like to see how quick a code can be.. check https://github.com/zibri/rand2 and see if you can optimize it further more... I got a top of 8 GB/s but that depends on many factors.. I wonder if it can be optimized more but I doubt it.. – Zibri Jun 29 '19 at 07:27
  • Definitively the fastest solution for deeply embedded system. And the hashmap is in flash then no RAM wasted – papyDoctor Jan 31 '21 at 08:10
  • 1) uint8_t pos - fails on all inputs longer than 255 bytes 2) calling strlen in for loop condition will cause dramatic performance degradation – Mightywill Dec 13 '21 at 14:48
13

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

Community
  • 1
  • 1
Mike M
  • 4,879
  • 5
  • 38
  • 58
9

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.

dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
5

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;
}
mgalela
  • 51
  • 1
  • 1
5

A fleshed out version of Michael Foukarakis 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);
        }
    }
}
Community
  • 1
  • 1
SMH
  • 59
  • 1
  • 1
3

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';
}
Henri Socha
  • 291
  • 2
  • 6
3

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
};
flcoder
  • 713
  • 4
  • 14
3

you can use this function which

  1. written with performance in mind (for embedded processors), no scanf, strtol or dynamic memory allocation
  2. has guard against output buffer overflow and odd input str len
/// in: valid chars are 0-9 + A-F + a-f
/// out_len_max==0: convert until the end of input string, out_len_max>0 only convert this many numbers
/// returns actual out size
int hexStr2Arr(unsigned char* out, const char* in, size_t out_len_max = 0)
{
    if (!out_len_max)
        out_len_max = 2147483647; // INT_MAX

    const int in_len = strnlen(in, out_len_max * 2);
    if (in_len % 2 != 0)
        return -1; // error, in str len should be even

    // calc actual out len
    const int out_len = out_len_max < (in_len / 2) ? out_len_max : (in_len / 2);

    for (int i = 0; i < out_len; i++) {
        char ch0 = in[2 * i];
        char ch1 = in[2 * i + 1];
        uint8_t nib0 = (ch0 & 0xF) + (ch0 >> 6) | ((ch0 >> 3) & 0x8);
        uint8_t nib1 = (ch1 & 0xF) + (ch1 >> 6) | ((ch1 >> 3) & 0x8);
        out[i] = (nib0 << 4) | nib1;
    }
    return out_len;
}

usage:

unsigned char result[128];
memset(result, 0, 128); // optional
printf("result len=%d\n", hexStr2Arr(result, "0a0B10"));  // result = [0A 0B 10 00 00 ...]

memset(result, 0, 128); // optional
// only convert single number
printf("result len=%d\n", hexStr2Arr(result, "0a0B10", 1)); // result = [0A 00 00 00 00 ...]
Ali80
  • 6,333
  • 2
  • 43
  • 33
2

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;
}
hutheano
  • 172
  • 2
  • 10
1
    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

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;
}
Community
  • 1
  • 1
runeks
  • 1,745
  • 2
  • 17
  • 26
1
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!

user411313
  • 3,930
  • 19
  • 16
1

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

// HexConverter.h
#ifndef HEXCONVERTER_H
#define HEXCONVERTER_H
unsigned int hexToByte (const char *hexString);
unsigned int hexToWord (const char *hexString);
#endif


// HexConverter.c
#include <string.h> // for strchr()
#include <ctype.h>  // for toupper()

unsigned int hexToByte (const char *hexString)
{
    unsigned int value;
    const char *hexDigits = "0123456789ABCDEF";

    value = 0;
    if (hexString != NULL)
    {
        char *ptr;

        ptr = strchr (hexDigits, toupper(hexString[0]));
        if (ptr != NULL)
        {
            value = (ptr - hexDigits) << 4;

            ptr = strchr (hexDigits, toupper(hexString[1]));
            if (ptr != NULL)
            {
                value = value | (ptr - hexDigits);
            }
        }
    }

    return value;
}

unsigned int hexToWord (const char *hexString)
{
    unsigned int value;

    value = 0;
    if (hexString != NULL)
    {
        value = (hexToByte (&hexString[0]) << 8) |
                (hexToByte (&hexString[2]));
    }

    return value;
}


// HexConverterTest.c
#include <stdio.h>

#include "HexConverter.h"

int main (int argc, char **argv)
{
    (void)argc; // not used
    (void)argv; // not used

    unsigned int value;
    char *hexString;

    hexString = "2a";
    value = hexToByte (hexString);
    printf ("%s == %x (%u)\n", hexString, value, value);

    hexString = "1234";
    value = hexToWord (hexString);
    printf ("%s == %x (%u)\n", hexString, value, value);

    hexString = "0102030405060708090a10ff";
    printf ("Hex String: %s\n", hexString);
    for (unsigned int idx = 0; idx < strlen(hexString); idx += 2)
    {
        value = hexToByte (&hexString[idx]);
        printf ("%c%c == %x (%u)\n", hexString[idx], hexString[idx+1],
                value, value);
    }

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

}
Amol M Kulkarni
  • 21,143
  • 34
  • 120
  • 164
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;
}
Geremia
  • 4,745
  • 37
  • 43
  • You have a lovely off-by-one error in `*(decodedPtr+1) = '\0'` (because `decodedPtr` was already incremented after the last write) – rluba Apr 14 '16 at 09:02
0

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;
}
IMAN4K
  • 1,265
  • 3
  • 24
  • 42
0

For newbies and to improve @Michael's answer here's a full version:

// out needs to be at least len/2+1 bytes long
// length will be returned 
int hexStrToBin(char* out, const char *str)
{
    int i, len = strlen(str);

    for (i = 0; i < len; i++)
    {
        // Reads str & stores in op
        sscanf(str, "%2hhx", &op[i]);
        str += 2;
    }

    return len/2;
}
Thom Wiggers
  • 6,938
  • 1
  • 39
  • 65
Daniel Selvan
  • 959
  • 10
  • 23
0
/**
 * @brief Convert a hex string to a byte array
 * @param hex pointer to the hex string to convert
 * @param output Array to place the results, should be half the size of the input array
 * @param array_length Number of bytes to output
 */
void hex_to_bytes(char *hex, uint8_t *output, uint8_t array_length)
{
    for (int i = 0, j = 0; i < array_length; i++, j += 2)
    {
        hex[j] = toupper(hex[j]);
        hex[j+1] = toupper(hex[j+1]);

        uint8_t bottom = hex[j + 1] - (hex[j + 1] > '9' ? 'A' - 10 : '0');
        uint8_t top = hex[j] - (hex[j] > '9' ? 'A' - 10 : '0');
        output[i] = (top * 16) + bottom;
    }
}
grahas
  • 35
  • 5
-1

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

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • 1
    nothing asked on SO should have "trivial" in the response, otherwise the question would have no means to exist – Valerio Nov 25 '18 at 22:22
  • and oliver clearly doesn't know it otherwise he would have posted the "trivial" loop in the answer :) – Zibri May 22 '19 at 07:21
-1

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".

Zibri
  • 9,096
  • 3
  • 52
  • 44
-1

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;
}  
LinconFive
  • 1,718
  • 1
  • 19
  • 24