将ascii十六进制字符串转换为字节数组

时间:2014-01-29 06:38:43

标签: c gcc

我有一个char数组说char value []={'0','2','0','c','0','3'};

我想将其转换为像unsigned char val[]={'02','0c','03'}

这样的字节数组

这是在嵌入式应用程序中,所以我不能使用string.h函数。我怎么能这样做?

4 个答案:

答案 0 :(得分:4)

你在谈论嵌入式应用程序时我假设您希望将数字保存为值而不是字符串/字符。因此,如果您只想将字符数据存储为数字(例如以整数形式存储),则可以使用sscanf

这意味着您可以执行以下操作:

 char source_val[] = {'0','A','0','3','B','7'} // Represents the numbers 0x0A, 0x03 and 0xB7
 uint8 dest_val[3];                            // We want to save 3 numbers
 for(int i = 0; i<3; i++)
 {
     sscanf(&source_val[i*2],"%x%x",&dest_val[i]); // Everytime we read two chars --> %x%x
 }
 // Now dest_val contains 0x0A, 0x03 and 0xB7

但是,如果您想将其存储为字符串(如示例所示),则无法使用unsigned char 因为这种类型也只有8位长,这意味着它只能存储一个字符。在单个(无符号)字符中显示“B3”不起作用。

编辑:根据评论,目标是将传递的数据保存为数值。不幸的是,开启者的编译器不支持sscanf,这是最简单的方法。无论如何,由于这是(在我看来)最简单的方法,我将把这部分答案留在这里,并尝试在此编辑中添加更自定义的方法。

关于数据类型,如果你有uint8,实际上并不重要。即使我建议使用某种整数数据类型,您也可以将数据存储到unsigned char。这里的问题是,您传递的数据是字符/字母,您希望将其解释为数值。但是,角色的内部存储会有所不同。您可以查看ASCII Table,在那里可以检查每个字符的内部值。 例如:

char letter = 'A'; // Internally 0x41 
char number = 0x61; // Internally 0x64 - represents the letter 'a'

正如您所看到的,上部和下部之间也存在差异。

如果您这样做:

int myVal = letter;  //

myVal 不会代表值0xA(十进制10),它的值为0x41。

您无法使用sscanf这一事实意味着您需要自定义功能。首先,我们需要一种方法将一个字母转换为整数:

int charToInt(char letter)
{
    int myNumerical;
    // First we want to check if its 0-9, A-F, or a-f) --> See ASCII Table
    if(letter > 47 && letter < 58)
    {
        // 0-9
        myNumerical = letter-48;
        // The Letter "0" is in the ASCII table at position 48 -> meaning if we subtract 48 we get 0 and so on...
    }
    else if(letter > 64 && letter < 71)
    {
       // A-F
       myNumerical = letter-55 
       // The Letter "A" (dec 10) is at Pos 65 --> 65-55 = 10 and so on..
    }
    else if(letter > 96 && letter < 103)
    {
       // a-f
       myNumerical = letter-87
       // The Letter "a" (dec 10) is at Pos 97--> 97-87 = 10 and so on...
    }
    else
    {
       // Not supported letter...
       myNumerical = -1;
    }
    return myNumerical;
}

现在我们有办法将每个字符转换为数字。另一个问题是总是将两个字符附加在一起,但这很容易:

int appendNumbers(int higherNibble, int lowerNibble)
{
     int myNumber = higherNibble << 4;
     myNumber |= lowerNibbler;
     return myNumber;
    // Example: higherNibble = 0x0A, lowerNibble = 0x03;  -> myNumber 0 0xA3
    // Of course you have to ensure that the parameters are not bigger than 0x0F 
}

现在一切都是这样的:

 char source_val[] = {'0','A','0','3','B','7'} // Represents the numbers 0x0A, 0x03 and 0xB7
 int dest_val[3];                             // We want to save 3 numbers
 int temp_low, temp_high;
 for(int i = 0; i<3; i++)
 {
     temp_high = charToInt(source_val[i*2]);
     temp_low = charToInt(source_val[i*2+1]);
     dest_val[i] = appendNumbers(temp_high , temp_low);
 }

我希望我理解你的问题,这有助于......

答案 1 :(得分:3)

如果你有一个“正确”的数组,比如问题中声明的value,那么你就可以遍历它的大小来得到每个字符。如果您使用的是ASCII字母表(最有可能)的系统,则可以通过减去数字'0'将字符形式的十六进制数字转换为十进制数字(请参阅链接的ASCII用于理解原因的表格,并为字母减去'A''a'(确保当然没有字母高于'F')并添加10。

如果您拥有第一个十六进制数字的值,则以相同的方式转换第二个十六进制数字。将第一个值乘以16并添加第二个值。您现在有单字节值对应于字符形式的两个十六进制数字。


一些代码示例的时间:

/* Function which converts a hexadecimal digit character to its integer value */
int hex_to_val(const char ch)
{
    if (ch >= '0' && ch <= '9')
        return ch - '0';  /* Simple ASCII arithmetic */
    else if (ch >= 'a' && ch <= 'f')
        return 10 + ch - 'a';  /* Because hex-digit a is ten */
    else if (ch >= 'A' && ch <= 'F')
        return 10 + ch - 'A';  /* Because hex-digit A is ten */
    else
        return -1;  /* Not a valid hexadecimal digit */
}

...

/* Source character array */
char value []={'0','2','0','c','0','3'};

/* Destination "byte" array */
char val[3];

/* `i < sizeof(value)` works because `sizeof(char)` is always 1 */
/* `i += 2` because there is two digits per value */
/* NOTE: This loop can only handle an array of even number of entries */
for (size_t i = 0, j = 0; i < sizeof(value); i += 2, ++j)
{
    int digit1 = hex_to_val(value[i]);      /* Get value of first digit */
    int digit2 = hex_to_val(value[i + 1]);  /* Get value of second digit */

    if (digit1 == -1 || digit2 == -1)
        continue;  /* Not a valid hexadecimal digit */

    /* The first digit is multiplied with the base */
    /* Cast to the destination type */
    val[j] = (char) (digit1 * 16 + digit2);
}

for (size_t i = 0; i < 3; ++i)
    printf("Hex value %lu = %02x\n", i + 1, val[i]);

上面代码的输出是

Hex value 1 = 02
Hex value 2 = 0c
Hex value 3 = 03

关于ASCII算法的注释:字符'0'的ASCII值为48,字符'1'的ASCII值为49。因此'1' - '0'会产生1

答案 2 :(得分:2)

使用strtol()很容易:

#include <stdlib.h>
#include <assert.h>

void parse_bytes(unsigned char *dest, const char *src, size_t n)
{
    /** size 3 is important to make sure tmp is \0-terminated and
        the initialization guarantees that the array is filled with zeros */
    char tmp[3] = "";

    while (n--) {
        tmp[0] = *src++;
        tmp[1] = *src++;
        *dest++ = strtol(tmp, NULL, 16);
    }
}

int main(void)
{
    unsigned char d[3];
    parse_bytes(d, "0a1bca", 3);
    assert(d[0] == 0x0a);
    assert(d[1] == 0x1b);
    assert(d[2] == 0xca);
    return EXIT_SUCCESS;
}

如果没有(即使它不是来自string.h),你可以这样做:

int ctohex(char c)
{
    if (c >= '0' && c <= '9') {
        return c - '0';
    }
    switch (c) {
        case 'a':
        case 'A':
            return 0xa;

        case 'b':
        case 'B':
            return 0xb;

        /**
         * and so on
         */
    }
    return -1;
}

void parse_bytes(unsigned char *dest, const char *src, size_t n)
{
    while (n--) {
        *dest = ctohex(*src++) * 16;
        *dest++ += ctohex(*src++);
    }
}

答案 3 :(得分:1)

<德尔> 假设8位字节(实际上不是由C标准保证,但无处不在),`unsigned char`的范围是0..255,`signed char`的范围是-128..127。 ASCII是使用0-127范围内的值开发的7位代码,因此相同的值可以由两个`char`类型表示。

对于现在发现的将计数的十六进制字符串从ascii转换为无符号字节的任务,这是我的看法:

unsigned int atob(char a){
    register int b;
    b = a - '0';    // subtract '0' so '0' goes to 0 .. '9' goes to 9
    if (b > 9) b = b - ('A' - '0') + 10;  // too high! try 'A'..'F'
    if (b > 15) b = b - ('a' - 'A);  // too high! try 'a'..'f'
    return b;
}

void myfunc(const char *in, int n){
    int i;
    unsigned char *ba;
    ba=malloc(n/2);
    for (i=0; i < n; i+=2){
        ba[i/2] = (atob(in[i]) << 4) | atob(in[i+1]);
    }
    // ... do something with ba
}