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.

2
  • 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. Commented Apr 6, 2016 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? Commented Apr 6, 2016 at 14:39

4 Answers 4

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;'
Sign up to request clarification or add additional context in comments.

1 Comment

@chux Sure, why not. Refactoring is life, after all. Done, thanks.
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

Comments

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"

Comments

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'

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.