-2

I'm trying to create an ASN.1 BITSTRING

To start I have a long hex string like "abcd..." which I believe needs to become { 0xab, 0xcd ... } in the output but not sure where to start (sscanf() on 2 chars at a time?). Realized this after incorrectly converting to char[] with 1010101111001101 but this is not the binary output, it is the binary digits shown as a string.

How to convert a hexadecimal string to a binary string in C doesn't seem to work for me (the res array comes out empty).

EDIT1

Based on the comments (and before looking at the excellent answers) I came up with the following. Will let others comment before marking an answer.

char input[] = "abcd";
char output[MAX_LENGTH];

char c[3];
int p = 0;
int b = 0;
while (input[p])
{
    strncpy(c, input + p, 2);
    c[2] = '\0';
    p += 2;
    output[ b++ ] = strtol(c, NULL, 16);
}
Community
  • 1
  • 1
sce
  • 190
  • 1
  • 12
  • 1
    How long a string of hexadecimal digits? If it is 16 or fewer, you can use `strtoull()` to do the conversion. If it can be longer than 16 hex digits, then you need to identify how you're going to handle 'arbitrary precision' hex numbers. – Jonathan Leffler Jul 27 '15 at 06:51
  • It's good to add a link for reference, but it's recommended to add your own code, ip and expected op and actual op for better understanding. – Sourav Ghosh Jul 27 '15 at 06:51
  • 2
    How exactly we are supposed to help if you do not provide your code? – Ari0nhh Jul 27 '15 at 06:52
  • general idea: loop with an `unsigned char c` and an `char bytestr[9]` as buffers, process input char by char in groups of 2, subtract `'0'`, if bigger than `9` subtract `'a'-'9'`, for the first set c to it, shift 4 bits, add the second to c., then check the bits (e.g. in a loop checking `(c & 0x80)` and shifting afterwards) and place the digits in `bytestr[i]`, set `bytestr[8]` to 0 and `strcat()` to your output buffer that should be large enough. –  Jul 27 '15 at 07:11
  • All: please check the question post edit and reverse the hold/negative points if appropriate. – sce Jul 31 '15 at 21:14

3 Answers3

1

If you need to convert "ab" to 0xab you could simply write

bool StrToHexDigit (char digit, char *converted)
{
   bool retVal = true;

   if (digit > '9')
   {
       if ((digit >= 'a') && (digit < 'g'))
// OR  if ((digit > 0x60) && (digit < 'g'))
       {
          *converted = digit - 'a';
       }

       if ((digit >= 'A') && (digit < 'G'))
// OR  if ((digit > 0x40) && (digit < 'G'))
       {
          *converted = digit - 'A';
       }

       *converted += 10;
   }
   else if (digit > '0')
   {
      *converted = digit - '0';
   }
   else
   {
      retVal = false;
   }

   return retVal;
}

bool StrToHex8Bit ( char hi, char low, uint8_t *converted)
{
   bool retVal=StrToHexDigit(hi, &hi)

   if ( retVal == true)
   {
      retVal=StrToHexDigit(low, &low)

      *converted = (hi<<4) | low;
   }

   return retVal;
}

You could change the StrToHexDigit to be faster, eg:

   if (digit > '9')
   {
       if ((digit > 0x60) && (digit < 'g'))
       {
          *converted = digit - 0x57;
       }

       if ((digit > 0x40) && (digit < 'G'))
       {
          *converted = digit - 0x37;
       }
   }

Another faster way, if you are sure about digits of your string (to avoid to check that digits are accettable), is to use a lookup table:

char lookup[255];

void init_lookup ( void )
{
   lookup['0'] = 0;
   lookup['1'] = 1;
   lookup['2'] = 2;
   lookup['3'] = 3;
   lookup['4'] = 4;
   lookup['5'] = 5;
   lookup['6'] = 6;
   lookup['7'] = 7;
   lookup['8'] = 8;
   lookup['9'] = 9;

   lookup['A'] = 10;
   lookup['B'] = 11;
   lookup['C'] = 12;
   lookup['D'] = 13;
   lookup['E'] = 14;
   lookup['F'] = 15;

   lookup['a'] = 10;
   lookup['b'] = 11;
   lookup['c'] = 12;
   lookup['d'] = 13;
   lookup['e'] = 14;
   lookup['f'] = 15;
}

uint8_t StrToHex (char hi, char low)
{
   return ((lookup[hi]<<4) | lookup[low]);
}
LPs
  • 16,045
  • 8
  • 30
  • 61
  • Downvoter: a comment??? – LPs Jul 27 '15 at 08:04
  • Well I didn't downvote. Nevertheless a comment: This last alternative looks to me like an over-optimization of something that isn't critical at all, wasting memory for no gain and (depending on the architecture) being still a bit slow because of unaligned access. –  Jul 27 '15 at 08:30
  • @FelixPalmen Yes, but if you think this implementation in a little 8 bit PIC with 8K flash, could save your app... – LPs Jul 27 '15 at 08:35
  • for any 8bit arch, this is indeed the fastest possible solution. I just don't think it's in the scope of OP's question ;) –  Jul 27 '15 at 08:37
  • @FelixPalmen yes, you right. I'm a little bit intrested on your comment about unaligned acces. Do you think change the type of lookup table to int could speed it up? – LPs Jul 27 '15 at 08:44
  • Well, you never really know until you know both the architecture and your C implementation, but the potential is there. –  Jul 27 '15 at 08:54
  • It was probably down-voted because the question was flagged. This happens a lot. They flag the question, down-vote it, and then down-vote all the answers. It's petty and stupid. Basically what I've been told, its the responsibility of the answer-er to know if the questioner will be or should be flagged. Answering the question then shows that you are trying to beat the system. – ydobonebi Jul 27 '15 at 16:06
1

Assuming some C string of arbitrary length, containing only the characters 0-9 and A-F (or their lower case version), then this should be pretty easy to achieve:

A single digit in hex is 4 bits in binary, so two digits form a single byte, so we need length / 2 bytes to store the complete converted result:

uint8_t * hexstr_to_bin(char const * str) {
  size_t const length = strlen(str);
  uint8_t * result = malloc((length + 1) / 2);
  // ...
  return result;
}

Then one can iterate over the characters, one at a time, and convert it to it's binary representation:

size_t it;
for (it = 0; it < length; ++it) {
 char const c = str[it];
 uint8_t const bin = (c > '9') ? (tolower(c) - 'a' + 10) : (c - '0');
 // ...
}

And assign it to the correct, possibly shifted, location:

if (it % 2 == 0) {
 result[it / 2] = bin << 4;
} else {
 result[it / 2] |= bin;
}

Of course there are numerous possibilities of ordering the values in the results array. Above code is live here. (Though untested)

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
0

Just for reference, a simple implementation processing one nibble at a time as a pipe filter:

#include <stdio.h>

static char nibble[5];

int main()
{
    int c, i;

    nibble[4] = 0;

    while ((c = fgetc(stdin)) != EOF)
    {
        if (c == '\n') fputc(c, stdout);
        if (c < '0' || (c > '9' && c < 'A') || (c > 'F' && c < 'a') || c > 'f')
            goto cont;

        if (c >= 'a') c -= ('a' - '9' - 1);
        else if (c >= 'A') c -= ('A' - '9' - 1);
        c -= '0';

        for (i = 0; i < 4; ++i)
        {
            nibble[i] = c & 0x8 ? '1' : '0';
            c <<= 1;
        }

        fputs(nibble, stdout);
cont:;
    }
    return 0;
}

Result:

> echo "800f024a" | ./hex2bin
10000000000011110000001001001010