简体   繁体   中英

Convert two ASCII Hexadecimal Characters (Two ASCII bytes) in one byte

I want to convert two ASCII bytes to one hexadecimal byte. eg.

0x30 0x43 => 0x0C , 0x34 0x46 => 0x4F ...

The ASCII bytes are a number between 0 and 9 or a letter between A and F (upper case only), so between 0x30 ... 0x39 and 0x41 ... 0x46

I know how "to construct" 0x4F with the numbers 0x34 and 0x46 : 0x4F = 0x34 * 0x10 + 0x46

So, in fact, i would to convert one ASCII byte in hexadecimal value.

For that, i can build and array to assign the hexadecimal value to the ASCII char :

0x30 => 0x00
0x31 => 0x01
...
0x46 => 0x0F

But, maybe it have a most « proper » solution.

The program will be run on an AVR µC and is compiled with avr-gcc , so scanf() / printf() solutions aren't suitable.

Have you got an idea ? Thanks

i can't make sense of your examples, but if you want to convert a string containing hexadecimal ascii characters to its byte value (eg so the string "56" becomes the byte 0x56, you can use this (which assumes your system is using ASCII)

uint8_t*
hex_decode(const char *in, size_t len,uint8_t *out)
{
        unsigned int i, t, hn, ln;

        for (t = 0,i = 0; i < len; i+=2,++t) {

                hn = in[i] > '9' ? in[i] - 'A' + 10 : in[i] - '0';
                ln = in[i+1] > '9' ? in[i+1] - 'A' + 10 : in[i+1] - '0';

                out[t] = (hn << 4 ) | ln;
        }

        return out;
}

You'd use it like eg

char x[]="1234";
uint8_t res[2];
hex_decode(x,strlen(x),res);

And res (which must be at least half the length of the in parameter) now contains the 2 bytes 0x12,0x34

Note also that this code needs the hexadecimal letters AF to be capital, af won't do (and it doesn't do any error checking - so you'll have to pass it valid stuff).

You can use strtol() , which is part of avr-libc , or you can write just your specific case pretty easily:

unsigned char charToHexDigit(char c)
{
  if (c >= 'A')
    return c - 'A' + 10;
  else
    return c - '0';
}

unsigned char stringToByte(char c[2])
{
  return charToHexDigit(c[0]) * 16 + charToHexDigit(c[1]);
}

The task:

Convert a string containing hexadecimal ascii characters to its byte values so ascii "FF" becomes 0xFF and ascii "10" (x31x30x00) becomes 0x10

char asciiString[]="aaAA12fF";// input ascii hex string 
char result[4];               // byte equivalent of the asciiString (the size should be at half of asciiString[])

// the final result should be:

result[0] = 0xAA;
result[1] = 0xAA;       
result[2] = 0x12;
result[3] = 0xFF;

//1. Firt step: convert asciiString so it contains upper cases only:

// convert string to upper cases:
stringToUpperCases(asciiString);

use:

void stringToUpperCases(char *p)
{   
    for(int i=0; *(p+i) !='\0'; i++)
    {
        *(p+i) = (unsigned char) toupper( *(p+i) );
    }
}

//2. Convert a string containing hexadecimal ascii characters to its byte values:

// convert string to bytes:

int nrOfBytes = stringToBytes(asciiString,result);

//use:  
unsigned char charToHexDigit(char c)
{
if (c >= 'A')
    return (c - 'A' + 10);
else
    return (c - '0');
}

unsigned char ascii2HexToByte(char *ptr)
{
    return charToHexDigit( *ptr )*16 + charToHexDigit( *(ptr+1) );
}

int stringToBytes(char *string, char *result)
{
    int k=0;
    int strLen = strlen(string);

    for(int i = 0; i < strLen; i = i + 2)
    {
        result[k] = ascii2HexToByte( &string[i] );
        k++;
    }

    return k; // number of bytes in the result array 
}   

//3. print result:

printNrOfBytes(nrOfBytes, result);

// use:

void printNrOfBytes(int nr, char *p)
{
   for(int i= 0; i < nr; i++)
    {
        printf( "0x%02X ", (unsigned char)*(p+i) );
    }
    printf( "\n");
}

//4. The result should be:

0xAA 0xAA 0x12 0xFF

//5. This is the test program:

char asciiString[]="aaAA12fF"; // input ascii hex string 
char result[4];                // result  
// convert string to upper cases:
stringToUpperCases(asciiString);

// convert string to bytes
int nrOfBytes = stringToBytes(asciiString,result);

// print result:
printNrOfBytes(nrOfBytes, result);

// result:
//  0xAA 0xAA 0x12 0xFF

It's works, but could be much optimized !

inline uint8_t  twoAsciiByteToByte(const std::string& s)
{
    uint8_t r = 0;

    if (s.length() == 4)
    {
        uint8_t a = asciiToByte(s[0]);
        uint8_t b = asciiToByte(s[1]);
        uint8_t c = asciiToByte(s[2]);
        uint8_t d = asciiToByte(s[3]);

        int h = (a * 10 + b);
        int l = (c * 10 + d);

        if (s[0] == '3')
            h -= 30;
        else if (s[0] == '4')
            h -= 31;

        if (s[2] == '3')
            l -= 30;
        else if (s[2] == '4')
            l -= 31;

        r = (h << 4) | l;
    }

    return r;
}

Here's a version that works with both upper and lower-case hex strings:

void hex_decode(const char *in, size_t len, uint8_t *out)
{
  unsigned int i, hn, ln;
  char hc, lc;

  memset(out, 0, len);

  for (i = 0; i < 2*len; i += 2) {

    hc = in[i];
    if ('a' <= hc && hc <= 'f') hc = toupper(hc);
    lc = in[i+1];
    if ('a' <= lc && lc <= 'f') lc = toupper(lc);

    hn = hc > '9' ? hc - 'A' + 10 : hc - '0';
    ln = lc > '9' ? lc - 'A' + 10 : lc - '0';

    out[i >> 1] = (hn << 4 ) | ln;
  }
}

Converting 2 hex chars to a byte is done in two steps:

  1. Convert char a and b to their number (eg 'F' -> 0xF ), which is done in two big if else branches, that check if the char is in the range '0' to '9' , 'A' to 'F' or 'a' to 'f' .

  2. In the 2nd step the two numbers are joined by shifting a (largest value is 0xF ( 0b0000_FFFF )) 4 to the left ( a << 4 -> 0b1111_0000 ) and then apply the bitwise or operation on a and b ( (a << 4) | b ):

a: 0000_1111
b: 1111_0000
-> 1111_1111
#include <stdio.h>
#include <stdint.h>

#define u8 uint8_t
#define u32 uint32_t

u8 to_hex_digit(char a, char b) {
    u8 result = 0;

    if (a >= 0x30 && a <= 0x39) {
        result = (a - 0x30) << 4;
    } else if (a >= 0x41 && a <= 0x46) {
        result = (a - 0x41 + 10) << 4;
    } else if (a >= 0x61 && a <= 0x7A) {
        result = (a - 0x61 + 10) << 4;
    } else {
        printf("invalid hex digit: '%c'\n", a);
    }

    if (b >= 0x30 && b <= 0x39) {
        result |= b - 0x30;
    } else if (b >= 0x41 && b <= 0x46) {
        result |= b - 0x41 + 10;
    } else if (b >= 0x61 && b <= 0x7A) {
        result |= b - 0x61 + 10;
    } else {
        printf("invalid hex digit: '%c'\n", b);
    }

    return result;
}

u32 main() {
    u8 result = to_hex_digit('F', 'F');
    printf("0x%X (%d)\n", result, result);

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM