C中的十六进制字符串到字节数组

Sze*_*eri 54 c string

是否有任何标准的C函数可以从十六进制字符串转换为字节数组
我不想写自己的功能.

Mic*_*kis 63

据我所知,这样做没有标准功能,但以下列方式实现起来很简单:

#include <stdio.h>

int main(int argc, char **argv) {
    const char hexstring[] = "DEadbeef10203040b00b1e50", *pos = hexstring;
    unsigned char val[12];

     /* WARNING: no sanitization or error-checking whatsoever */
    for (size_t count = 0; count < sizeof val/sizeof *val; count++) {
        sscanf(pos, "%2hhx", &val[count]);
        pos += 2;
    }

    printf("0x");
    for(size_t count = 0; count < sizeof val/sizeof *val; count++)
        printf("%02x", val[count]);
    printf("\n");

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编辑

正如Al指出的那样,如果字符串中有十六进制数字的奇数,则必须确保 在前面添加0开头.例如,上述示例"f00f5"{0xf0, 0x0f, 0x05}错误地评估该字符串,而不是正确的{0x0f, 0x00, 0xf5}.

修改了一些示例来解决@MassimoCallegari的评论

  • 这是一个很好的方法,但请注意,如果十六进制字符串中有一个奇数位数,它将给出一个不正确的结果(隐含的零将在最后一个数字前面,而不是第一个数字,所以"5ab5c"将打印为0x5ab50c而不是0x05ab5c). (5认同)

Dav*_*dek 12

我通过谷歌搜索找到了同样的问题.我不喜欢调用sscanf()或strtol()的想法,因为它感觉有点矫枉过正.我写了一个快速函数,它不验证文本确实是字节流的十六进制表示,但是将处理奇数个十六进制数字:

uint8_t tallymarker_hextobin(const char * str, uint8_t * bytes, size_t blen)
{
   uint8_t  pos;
   uint8_t  idx0;
   uint8_t  idx1;

   // mapping of ASCII characters to hex values
   const uint8_t hashmap[] =
   {
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //  !"#$%&'
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ()*+,-./
     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567
     0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>?
     0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // @ABCDEFG
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // HIJKLMNO
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // PQRSTUVW
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // XYZ[\]^_
     0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // `abcdefg
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // hijklmno
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pqrstuvw
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // xyz{|}~.
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // ........
   };

   bzero(bytes, blen);
   for (pos = 0; ((pos < (blen*2)) && (pos < strlen(str))); pos += 2)
   {
      idx0 = (uint8_t)str[pos+0];
      idx1 = (uint8_t)str[pos+1];
      bytes[pos/2] = (uint8_t)(hashmap[idx0] << 4) | hashmap[idx1];
   };

   return(0);
}
Run Code Online (Sandbox Code Playgroud)

  • 你可以通过添加4个逻辑指令将mapsize减少到32个字节,这是一个很好的权衡:`idx0 =(uint8_t)str [pos + 0]&0x1F ^ 0x10;`和**idx1**相同​​.然后你可以删除`01234567`行之前的所有字节和'HIJKLMNO`行之后的所有字节 (6认同)
  • @zibri 运行 1,000,000 次迭代,我的代码平均需要 0.011 秒才能完成,你的代码平均需要 0.113 秒。我对矫枉过正的评论是指运行时效率。回顾这些评论,几乎没有关于如何改进我的代码的好建议,包括检查 pos 变量以进行循环。作为旁注,您应该始终在编写输出函数时包括边界检查,因为如果输入十六进制比输出缓冲区长,您的函数可能会导致缓冲区溢出。 (3认同)
  • sscanf和strtol是过大的,但是不是不必要的32行十六进制表吗? (2认同)

dmc*_*kee 8

对于短字符串,strtol,strtoll,并且strtoimax将工作得很好(注意,第三个参数是在处理字符串使用...它设定为16基).如果您的输入时间长于number-of-bits-in-the-longest-integer-type/4此时您将需要其他答案建议的更灵活的方法之一.


Mik*_*e M 7

除了上面的优秀答案,我会写一个不使用任何库的C函数,并且有一些防止坏字符串的保护.

uint8_t* datahex(char* string) {

    if(string == NULL) 
       return NULL;

    size_t slength = strlen(string);
    if((slength % 2) != 0) // must be even
       return NULL;

    size_t dlength = slength / 2;

    uint8_t* data = malloc(dlength);
    memset(data, 0, dlength);

    size_t index = 0;
    while (index < slength) {
        char c = string[index];
        int value = 0;
        if(c >= '0' && c <= '9')
          value = (c - '0');
        else if (c >= 'A' && c <= 'F') 
          value = (10 + (c - 'A'));
        else if (c >= 'a' && c <= 'f')
          value = (10 + (c - 'a'));
        else {
          free(data);
          return NULL;
        }

        data[(index/2)] += value << (((index + 1) % 2) * 4);

        index++;
    }

    return data;
}
Run Code Online (Sandbox Code Playgroud)

说明:

一个.index/2 | 整数之间的舍入将向下舍入该值,因此0/2 = 0,1/2 = 0,2/2 = 1,3/2 = 0等等.因此,对于每2个字符串字符,我们将值添加到1个数据字节.

湾 (指数+ 1)%2 | 我们希望奇数到1,甚至到0,因为十六进制字符串的第一个数字是最重要的,需要乘以16.所以对于索引0 => 0 + 1%2 = 1,索引1 => 1 + 1%2 = 0等

C.<< 4 | Shift by 4乘以16.示例:b00000001 << 4 = b00010000

  • 它甚至失败了.`if(slength%2 == 0)`应该是`if(slength%2!= 0)`.否则似乎工作. (2认同)

小智 5

通过对 user411313 的代码进行一些修改,以下对我有用:

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

int main ()
{
    char *hexstring = "deadbeef10203040b00b1e50";
    int i;
    unsigned int bytearray[12];
    uint8_t str_len = strlen(hexstring);

    for (i = 0; i < (str_len / 2); i++) {
        sscanf(hexstring + 2*i, "%02x", &bytearray[i]);
        printf("bytearray %d: %02x\n", i, bytearray[i]);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)


小智 5

Michael Foukarakis 帖子的充实版本:

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

void print(unsigned char *byte_array, int byte_array_size)
{
    int i = 0;
    printf("0x");
    for(; i < byte_array_size; i++)
    {
        printf("%02x", byte_array[i]);
    }
    printf("\n");
}

int convert(const char *hex_str, unsigned char *byte_array, int byte_array_max)
{
    int hex_str_len = strlen(hex_str);
    int i = 0, j = 0;

    // The output array size is half the hex_str length (rounded up)
    int byte_array_size = (hex_str_len+1)/2;

    if (byte_array_size > byte_array_max)
    {
        // Too big for the output array
        return -1;
    }

    if (hex_str_len % 2 == 1)
    {
        // hex_str is an odd length, so assume an implicit "0" prefix
        if (sscanf(&(hex_str[0]), "%1hhx", &(byte_array[0])) != 1)
        {
            return -1;
        }

        i = j = 1;
    }

    for (; i < hex_str_len; i+=2, j++)
    {
        if (sscanf(&(hex_str[i]), "%2hhx", &(byte_array[j])) != 1)
        {
            return -1;
        }
    }

    return byte_array_size;
}

void main()
{
    char *examples[] = { "", "5", "D", "5D", "5Df", "deadbeef10203040b00b1e50", "02invalid55" };
    unsigned char byte_array[128];
    int i = 0;

    for (; i < sizeof(examples)/sizeof(char *); i++)
    {
        int size = convert(examples[i], byte_array, 128);
        if (size < 0)
        {
            printf("Failed to convert '%s'\n", examples[i]);
        }
        else if (size == 0)
        {
            printf("Nothing to convert for '%s'\n", examples[i]);
        }
        else
        {
            print(byte_array, size);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)