user188728
user188728

Reputation:

Hex to char array in C

Given a string of hex values i.e. e.g. "0011223344" so that's 0x00, 0x11 etc.

How do I add these values to a char array?

Equivalent to say:

char array[4] = { 0x00, 0x11 ... };

Upvotes: 11

Views: 72588

Answers (11)

Jay Sullivan
Jay Sullivan

Reputation: 18259

Below are my hex2bin and bin2hex implementations.

These functions:

  • Are public domain (feel free to copy and paste)
  • Are simple
  • Are correct (i.e., tested)
  • Perform error handling (-1 means invalid hex string)

hex2bin

static char h2b(char c) {
    return '0'<=c && c<='9' ? c - '0'      :
           'A'<=c && c<='F' ? c - 'A' + 10 :
           'a'<=c && c<='f' ? c - 'a' + 10 :
           /* else */         -1;
}

int hex2bin(unsigned char* bin,  unsigned int bin_len, const char* hex) {
    for(unsigned int i=0; i<bin_len; i++) {
        char b[2] = {h2b(hex[2*i+0]), h2b(hex[2*i+1])};
        if(b[0]<0 || b[1]<0) return -1;
        bin[i] = b[0]*16 + b[1];
    }
    return 0;
}

bin2hex

static char b2h(unsigned char b, int upper) {
    return b<10 ? '0'+b : (upper?'A':'a')+b-10;
}

void bin2hex(char* hex, const unsigned char* bin, unsigned int bin_len, int upper) {
    for(unsigned int i=0; i<bin_len; i++) {
        hex[2*i+0] = b2h(bin[i]>>4,   upper);
        hex[2*i+1] = b2h(bin[i]&0x0F, upper);
    }
}

Upvotes: 0

Zibri
Zibri

Reputation: 9837

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" (non hex, non space).

Note: also this is a valid string:

"01 2 03 04 ab Cd eF garbage AB"

and produces:

01 02 03 04 ab cd ef

Upvotes: 1

mckenzm
mckenzm

Reputation: 1820

Let's say this is a little-endian ascii platform. Maybe the OP meant "array of char" rather than "string".. We work with pairs of char and bit masking.. note shiftyness of x16..

/* not my original work, on stacko somewhere ? */

for (i=0;i < 4;i++) {

    char a = string[2 * i];
    char b = string[2 * i + 1];

    array[i] = (((encode(a) * 16) & 0xF0) + (encode(b) & 0x0F));
 }

and function encode() is defined...

unsigned char encode(char x) {     /* Function to encode a hex character */
/****************************************************************************
 * these offsets should all be decimal ..x validated for hex..              *
 ****************************************************************************/
    if (x >= '0' && x <= '9')         /* 0-9 is offset by hex 30 */
        return (x - 0x30);
    else if (x >= 'a' && x <= 'f')    /* a-f offset by hex 57 */
        return(x - 0x57);
    else if (x >= 'A' && x <= 'F')    /* A-F offset by hex 37 */
        return(x - 0x37);
}

This approach floats around elsewhere, it is not my original work, but it is old. Not liked by the purists because it is non-portable, but extension would be trivial.

Upvotes: 1

Rudra
Rudra

Reputation: 364

I was searching for the same thing and after reading a lot, finally created this function. Thought it might help, someone

// in = "63 09  58  81" 
void hexatoascii(char *in, char* out, int len){
    char buf[5000];
    int i,j=0;
    char * data[5000];
    printf("\n size %d", strlen(in));
    for (i = 0; i < strlen(in); i+=2)
    {
        data[j] = (char*)malloc(8);
        if (in[i] == ' '){
            i++;
        }
        else if(in[i + 1] == ' '){
            i++;
        }
        printf("\n %c%c", in[i],in[i+1]);
        sprintf(data[j], "%c%c", in[i], in[i+1]);
        j++;
    }

    for (i = 0; i < j-1; i++){
        int tmp;
        printf("\n data %s", data[i] );
        sscanf(data[i], "%2x", &tmp);
        out[i] = tmp;
    }
    //printf("\n ascii value of hexa %s", out);
}

Upvotes: 1

Paul Nathan
Paul Nathan

Reputation: 40309

First, your question isn't very precise. Is the string a std::string or a char buffer? Set at compile-time?

Dynamic memory is almost certainly your answer.

char* arr = (char*)malloc(numberOfValues);

Then, you can walk through the input, and assign it to the array.

Upvotes: -4

Andrew Stern
Andrew Stern

Reputation: 698

{
    char szVal[] = "268484927472";
    char szOutput[30];

    size_t nLen = strlen(szVal);
    // Make sure it is even.
    if ((nLen % 2) == 1)
    {
        printf("Error string must be even number of digits %s", szVal);
    }

    // Process each set of characters as a single character.
    nLen >>= 1;
    for (size_t idx = 0; idx < nLen; idx++)
    {
        char acTmp[3];
        sscanf(szVal + (idx << 1), "%2s", acTmp);
        szOutput[idx] = (char)strtol(acTmp, NULL, 16);
    }
}

Upvotes: 0

Test
Test

Reputation: 1727

Give a best way:

Hex string to numeric value , i.e. str[] = "0011223344" to value 0x0011223344, use

value = strtoul(string, NULL, 16); // or strtoull()

done. if need remove beginning 0x00, see below.

though for LITTLE_ENDIAN platforms, plus: Hex value to char array, value 0x11223344 to char arr[N] = {0x00, 0x11, ...}

unsigned long *hex = (unsigned long*)arr;
*hex = htonl(value);
// you'd like to remove any beginning 0x00
char *zero = arr;
while (0x00 == *zero) { zero++; }
if (zero > arr) memmove(zero, arr, sizeof(arr) - (zero - arr));

done.

Notes: For converting long string to a 64 bits hex char arr on a 32-bit system, you should use unsigned long long instead of unsigned long, and htonl is not enough, so do it yourself as below because might there's no htonll, htonq or hton64 etc:

#if __KERNEL__
    /* Linux Kernel space */
    #if defined(__LITTLE_ENDIAN_BITFIELD)
        #define hton64(x)   __swab64(x)
    #else
        #define hton64(x)   (x)
    #endif
#elif defined(__GNUC__)
    /* GNU, user space */
    #if __BYTE_ORDER == __LITTLE_ENDIAN 
        #define hton64(x)   __bswap_64(x)
    #else
        #define hton64(x)   (x)
    #endif
#elif 
         ...
#endif

#define ntoh64(x)   hton64(x)

see http://effocore.googlecode.com/svn/trunk/devel/effo/codebase/builtin/include/impl/sys/bswap.h

Upvotes: 0

Jonathan Leffler
Jonathan Leffler

Reputation: 753890

You can't fit 5 bytes worth of data into a 4 byte array; that leads to buffer overflows.

If you have the hex digits in a string, you can use sscanf() and a loop:

#include <stdio.h>
#include <ctype.h>

int main()
{
    const char *src = "0011223344";
    char buffer[5];
    char *dst = buffer;
    char *end = buffer + sizeof(buffer);
    unsigned int u;

    while (dst < end && sscanf(src, "%2x", &u) == 1)
    {
        *dst++ = u;
        src += 2;
    }

    for (dst = buffer; dst < end; dst++)
        printf("%d: %c (%d, 0x%02x)\n", dst - buffer,
               (isprint(*dst) ? *dst : '.'), *dst, *dst);

    return(0);
}

Note that printing the string starting with a zero-byte requires care; most operations terminate on the first null byte. Note that this code did not null-terminate the buffer; it is not clear whether null-termination is desirable, and there isn't enough space in the buffer I declared to add a terminal null (but that is readily fixed). There's a decent chance that if the code was packaged as a subroutine, it would need to return the length of the converted string (though you could also argue it is the length of the source string divided by two).

Upvotes: 18

sambowry
sambowry

Reputation: 2466

If the string is correct and no need to keep its content then i would do it this way:

#define hex(c) ((*(c)>='a')?*(c)-'a'+10:(*(c)>='A')?*(c)-'A'+10:*(c)-'0') 

void hex2char( char *to ){
  for(char *from=to; *from; from+=2) *to++=hex(from)*16+hex(from+1);
  *to=0;
}

EDIT 1: sorry, i forget to calculate with the letters A-F (a-f)

EDIT 2: i tried to write a more pedantic code:

#include <string.h> 

int xdigit( char digit ){
  int val;
       if( '0' <= digit && digit <= '9' ) val = digit -'0';
  else if( 'a' <= digit && digit <= 'f' ) val = digit -'a'+10;
  else if( 'A' <= digit && digit <= 'F' ) val = digit -'A'+10;
  else                                    val = -1;
  return val;
}

int xstr2str( char *buf, unsigned bufsize, const char *in ){
  if( !in ) return -1; // missing input string

  unsigned inlen=strlen(in);
  if( inlen%2 != 0 ) return -2; // hex string must even sized

  for( unsigned i=0; i<inlen; i++ )
    if( xdigit(in[i])<0 ) return -3; // bad character in hex string

  if( !buf || bufsize<inlen/2+1 ) return -4; // no buffer or too small

  for( unsigned i=0,j=0; i<inlen; i+=2,j++ )
    buf[j] = xdigit(in[i])*16 + xdigit(in[i+1]);

  buf[inlen/2] = '\0';
  return inlen/2+1;
}

Testing:

#include <stdio.h> 

char buf[100] = "test";

void test( char *buf, const char *s ){
   printf("%3i=xstr2str( \"%s\", 100, \"%s\" )\n", xstr2str( buf, 100, s ), buf, s );
}

int main(){
  test( buf,      (char*)0   );
  test( buf,      "123"      );
  test( buf,      "3x"       );
  test( (char*)0, ""         );
  test( buf,      ""         );
  test( buf,      "3C3e"     );
  test( buf,      "3c31323e" );

  strcpy( buf,    "616263"   ); test( buf, buf );
}

Result:

 -1=xstr2str( "test", 100, "(null)" )
 -2=xstr2str( "test", 100, "123" )
 -3=xstr2str( "test", 100, "3x" )
 -4=xstr2str( "(null)", 100, "" )
  1=xstr2str( "", 100, "" )
  3=xstr2str( "", 100, "3C3e" )
  5=xstr2str( "", 100, "3c31323e" )
  4=xstr2str( "abc", 100, "abc" )

Upvotes: 3

Bill Forster
Bill Forster

Reputation: 6297

I would do something like this;

// Convert from ascii hex representation to binary
// Examples;
//   "00" -> 0
//   "2a" -> 42
//   "ff" -> 255
// Case insensitive, 2 characters of input required, no error checking
int hex2bin( const char *s )
{
    int ret=0;
    int i;
    for( i=0; i<2; i++ )
    {
        char c = *s++;
        int n=0;
        if( '0'<=c && c<='9' )
            n = c-'0';
        else if( 'a'<=c && c<='f' )
            n = 10 + c-'a';
        else if( 'A'<=c && c<='F' )
            n = 10 + c-'A';
        ret = n + ret*16;
    }
    return ret;
}

int main()
{
    const char *in = "0011223344";
    char out[5];
    int i;

    // Hex to binary conversion loop. For example;
    // If in="0011223344" set out[] to {0x00,0x11,0x22,0x33,0x44}
    for( i=0; i<5; i++ )
    {
        out[i] = hex2bin( in );
        in += 2;
    }
    return 0;
}

Upvotes: 3

DigitalZebra
DigitalZebra

Reputation: 41503

Fatalfloor...

There are a couple of ways to do this... first, you can use memcpy() to copy the exact representation into the char array.

You can use bit shifting and bit masking techniques as well. I'm guessing this is what you need to do as it sounds like a homework problem.

Lastly, you can use some fancy pointer indirection to copy the memory location you need.

All of these methods are detailed here:

Store an int in a char array?

Upvotes: 0

Related Questions