2

I have an input like

char *input="00112233FFAA";
uint8_t output[6];

What is the easiest way to convert input into output with sscanf? (prefer 1 line with no loop) The solution I have in mind doesn't scale to 20+ hex string.

sscanf(input, "%x%x%x%x%x",output[0], output[1]....output[5]);
Wei Shi
  • 4,945
  • 8
  • 49
  • 73

4 Answers4

12

Why scanf if this can be easily written by hand:

const size_t numdigits = strlen(input) / 2;

uint8_t * const output = malloc(numdigits);

for (size_t i = 0; i != numdigits; ++i)
{
  output[i] = 16 * toInt(input[2*i]) + toInt(intput[2*i+1]);
}

unsigned int toInt(char c)
{
  if (c >= '0' && c <= '9') return      c - '0';
  if (c >= 'A' && c <= 'F') return 10 + c - 'A';
  if (c >= 'a' && c <= 'f') return 10 + c - 'a';
  return -1;
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • The standard doesn't technically guarantee consecutive alphabet characters, but within the range A-F your code should work in EBCDIC on the two computers still using that. – Chris Lutz Nov 19 '11 at 22:32
  • 1
    @ChrisLutz: Indeed, there are a few assumptions there about the permissible encodings. Worst case you can replace the `toInt` body by a lookup table... – Kerrek SB Nov 19 '11 at 22:33
5

If you do not want to use a loop, then you need to explicitly write out all six (or twenty) array locations (although %x is not the correct conversion character - it expects a pointer to unsigned int as its corresponding argument). If you don't want to write them all out, then you need to use a loop - it can be quite simple, though:

for (i = 0; i < 6; i++)
    sscanf(&input[i * 2], "%2hhx", &output[i]);
caf
  • 233,326
  • 40
  • 323
  • 462
  • 1
    The `hh` didn't do the trick for me. I had to parse into a temporary array of `unsigned int` and then copy manually to the `uint8_t` array. – ChrisWue Mar 14 '14 at 03:48
  • 2
    @ChrisWue: Probably you don't have a C99-conforming standard library (eg. the Microsoft C library). – caf Mar 14 '14 at 05:30
3

Here is an alternate implementation.

#include <stdio.h>
#include <stdint.h>

#define _base(x) ((x >= '0' && x <= '9') ? '0' : \
         (x >= 'a' && x <= 'f') ? 'a' - 10 : \
         (x >= 'A' && x <= 'F') ? 'A' - 10 : \
            '\255')
#define HEXOF(x) (x - _base(x))

int main() {
    char input[] = "00112233FFAA";
    char *p;
    uint8_t *output;

    if (!(sizeof(input) & 1)) { /* even digits + \0 */
        fprintf(stderr,
            "Cannot have odd number of characters in input: %d\n",
            sizeof(input));
        return -1;
    }

    output = malloc(sizeof(input) >> 1);

    for (p = input; p && *p; p+=2 ) {
            output[(p - input) >> 1] =
            ((HEXOF(*p)) << 4) + HEXOF(*(p+1));
    }

    return 0;
}
Ahmed Masud
  • 21,655
  • 3
  • 33
  • 58
0

@caf already had a good simple idea.

However I had to use %02x and now it's working fine:

for (i = 0; i < 6; i++)
    sscanf(&input[i * 2], "%02x", &output[i]);
Philip H
  • 1
  • 1