2

I need to convert an Hex string to an Ascii string. Not only display it with a printf but also to save it in memory. I use this code to display it but I would like to save it. How can I do?:

#include <stdio.h>
#include <string.h>

int hex_to_int(char c){
        int first = c / 16 - 3;
        int second = c % 16;
        int result = first*10 + second;
        if(result > 9) result--;
        return result;
}

int hex_to_ascii(char c, char d){
        int high = hex_to_int(c) * 16;
        int low = hex_to_int(d);
        return high+low;
}

int main(){
        const char st[12] = "48656C6C6F3B";
        int length = strlen(st);
        int i;
        char buf = 0;
        for(i = 0; i < length; i++){
                if(i % 2 != 0){
                        printf("%c", hex_to_ascii(buf, st[i]));
                }else{
                        buf = st[i];
                }
        }
}

My first solution was to use sprintf(buf,"%c",hex_to_ascii(buf,st[i]) int the for loop, but this solution doesn't work because sprintf need a pointer of char.

wiredniko
  • 2,491
  • 3
  • 19
  • 37
  • 2
    `sprintf()` certainly doesn't need a pointer for the `%c` conversion. But if you have the character that's all you need, just store it. – unwind Apr 06 '16 at 14:02
  • When converting a " Hex string" to an "ASCII string", the design depends on things not defined here: 1) Will the hex string always be well-formed and if not what should the result be? 2) Handle A-F and a-f? 3) What allocates the buffer for the resultant string? 4) How to handle "00"? 5) Portable to non-ASCII? – chux - Reinstate Monica Apr 06 '16 at 14:39

4 Answers4

3

Okay, let's have a shot:

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

static unsigned char hexdigit2int(unsigned char xd)
{
  if (xd <= '9')
    return xd - '0';
  xd = tolower(xd);
  if (xd == 'a')
    return 10;
  if (xd == 'b')
    return 11;
  if (xd == 'c')
    return 12;
  if (xd == 'd')
    return 13;
  if (xd == 'e')
    return 14;
  if (xd == 'f')
    return 15;
  return 0;
}

int main(void)
{
  const char st[] = "48656C6C6F3B", *src = st;
  char text[sizeof st + 1], *dst = text;

  while (*src != '\0')
  {
    const unsigned char high = hexdigit2int(*src++);
    const unsigned char low  = hexdigit2int(*src++);
    *dst++ = (high << 4) | low;
  }
  *dst = '\0';
  printf("Converted '%s', got '%s'\n", st, text);
  return 0;
}

The very verbose-looking digit2int() function is trying to not be ASCII-dependent, which is always nice. Note that the loop in main() assumes proper input, it only checks for termination every two characters and won't handle non-hex data.

Oh, and this prints:

Converted '48656C6C6F3B', got 'Hello;'
unwind
  • 391,730
  • 64
  • 469
  • 606
0

as you probably know each character is represented by an int --> ...'a' = 97, 'b'=98... so once you convert an hex to int there is no need to create another function converting it to ascii - just by storing the integer into a character will do the trick (unless I'm missing something understanding the exercise).

as for storing it back to memory: there are many options: 1. remove the const before const char st[12] = "48656C6C6F3B"; and assign the return value from the function to the desired cell or using sscanf

take a look here

Community
  • 1
  • 1
Kukula Mula
  • 1,788
  • 4
  • 19
  • 38
0

You did everything well but there are some issues. Compare with the following:

const char* st = "48656C6C6F3B"; // I don't want to calculate storage size
char r[12] = { 0 }; // Here is I want to store the result
char* h = r; // pointer to write position in the result
int length = strlen(st); 
for (int i = 0; i < length; i+=2) {
  assert((h - r) < sizeof(r)); // check that there is not problem with out of range
  *h++ = hex_to_ascii(st[i], st[i + 1]);
}
printf("%s", r); // now the r contains "ansi string"
AnatolyS
  • 4,249
  • 18
  • 28
0

To "save" the result, simple add a buffer, dest, to store the result.

Additional suggestions included in code.

// add void to signature
int main(void) {
  const char st[12] = "48656C6C6F3B";
  int length = strlen(st);
  int i;
  char buf = 0;

  // add destination buffer
  char dest[10];

  // Add test
  // for (i = 0; i < length; i++) {
  for (i = 0; i < length && (i/2 + 1) < sizeof(dest); i++) {
    if (i % 2 != 0) {

      // printf("%c", hex_to_ascii(buf, st[i]));
      dest[i/2] = hex_to_ascii(buf, st[i]);

    } else {
      buf = st[i];
    }
  }

  // Add termination
  dest[i/2] = '\0';
  // Do someting with dest
  puts(dest);
  return 0;
}

Alternatively, some code that handles various possible issues: lower/upper case hex digits, invalid characters, odd count, small buffer, embedded null character.

#include <stdlib.h>
#include <string.h>
// There are _many_ ways to do this step.
unsigned char2digit(int ch) {
  static const char Hex[] = "0123456789ABCDEF0123456789abcdef";
  char *p = memchr(Hex, ch, 32);
  if (p) {
    return (unsigned) (p - Hex) % 16;
  }
  return (unsigned) -1;  // error value
}

// Return NULL with ill-formed string
char *HexStringToString(char *dest, size_t size, const char *src) {
  char *p = dest;
  if (size <= 0) {
    return NULL;
  }
  size--;
  while (*src) {
    if (size == 0) return NULL;
    size--;

    unsigned msb = char2digit(*src++);
    if (msb > 15) return NULL;
    unsigned lsb = char2digit(*src++);
    if (lsb > 15) return NULL;
    char ch = (char) (msb * 16 + lsb);

    // Optionally test for embedded null character
    if (ch == 0) return NULL;

    *p++ = ch;
  }
  *p = '\0';
  return dest;
}

void testHex(const char *s) {
  char buf[10];
  char *dest = HexStringToString(buf, sizeof buf, s);
  printf("%-24s --> '%s'\n", s, dest ? dest : "NULL");
}

#include <stdio.h>
int main(void) {
  testHex("48656C6C6F3B");        // upper case
  testHex("48656c6c6f3b");        // lower case
  testHex("");
  testHex("48656C6C6F3B48656C");
  // fails
  testHex("48656C6C6F3B48656C6C"); // Too long
  testHex("48656C6C6F3B0");        // Odd character count
  testHex("48656C6C6F3Bxx");       // Non-hex character
  testHex("48006C6C6F3B");         // null character
  return 0;
}        

Output

48656C6C6F3B             --> 'Hello;'
48656c6c6f3b             --> 'Hello;'
                         --> ''
48656C6C6F3B48656C       --> 'Hello;Hel'
48656C6C6F3B48656C6C     --> 'NULL'
48656C6C6F3B0            --> 'NULL'
48656C6C6F3Bxx           --> 'NULL'
48006C6C6F3B             --> 'NULL'
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256