如何在C语言中正确将十六进制字符串转换为字节数组?

5

我需要将一个包含十六进制值的字符串转换为字节数组。虽然这个问题已经在这里得到了第一个答案,但是我遇到了以下错误:

warning: ISO C90 does not support the ‘hh’ gnu_scanf length modifier [-Wformat]

由于我不喜欢警告,而省略hh只会创建另一个警告。

warning: format ‘%x’ expects argument of type ‘unsigned int *’, but argument 3 has type ‘unsigned char *’ [-Wformat]

我的问题是:如何正确地做到这一点?为了完整起见,我再次在此处发布示例代码:
#include <stdio.h>

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

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

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

    return(0);
}

1
考虑使用strtol,这可能会有所帮助。 - bash.d
没有什么问题是一个额外的变量解决不了的。 - Hans Passant
4个回答

4
您可以使用 strtol() 代替。
只需将此行替换为:
sscanf(pos, "%2hhx", &val[count]);

使用:

char buf[10];
sprintf(buf, "0x%c%c", pos[0], pos[1]);
val[count] = strtol(buf, NULL, 0);

更新: 您可以使用以下片段来避免使用 sprintf():

char buf[5] = {"0", "x", pos[0], pos[1], 0};
val[count] = strtol(buf, NULL, 0);

1
更新的答案避免使用sprintf - mvp
4
可以将数字16作为strtol的第三个参数传递,而无需使用前缀“0x”。 - Vovanium
我正在解决一个非常相似的问题,使用@Vovanium提到的基数16strtol进行处理,现在我已经能够像@mvp提到的那样摆脱sprintf。代码片段如下: “char t [5] = {fd,sd,0}; d [c] = strtol(t,NULL,16);” - zevarito

2

你可以选择将编译器切换到C99模式(hh长度修饰符在C99中已标准化),或者使用一个unsigned int临时变量:

unsigned int byteval;
if (sscanf(pos, "%2x", &byteval) != 1)
{
    /* format error */
}
val[count] = byteval;

2
不使用sscanf,strol等方法进行转换是否可行?下面是HexToBin函数以及一个免费的BinToHex函数。(请注意,最初使用的是枚举错误代码返回错误日志系统,而不是简单的-1返回。)
unsigned char HexChar (char c)
{
    if ('0' <= c && c <= '9') return (unsigned char)(c - '0');
    if ('A' <= c && c <= 'F') return (unsigned char)(c - 'A' + 10);
    if ('a' <= c && c <= 'f') return (unsigned char)(c - 'a' + 10);
    return 0xFF;
}

int HexToBin (const char* s, unsigned char * buff, int length)
{
    int result;
    if (!s || !buff || length <= 0) return -1;

    for (result = 0; *s; ++result)
    {
        unsigned char msn = HexChar(*s++);
        if (msn == 0xFF) return -1;
        unsigned char lsn = HexChar(*s++);
        if (lsn == 0xFF) return -1;
        unsigned char bin = (msn << 4) + lsn;

        if (length-- <= 0) return -1;
        *buff++ = bin;
    }
    return result;
}

void BinToHex (const unsigned char * buff, int length, char * output, int outLength)
{
    char binHex[] = "0123456789ABCDEF";

    if (!output || outLength < 4) return;
    *output = '\0';

    if (!buff || length <= 0 || outLength <= 2 * length)
    {
        memcpy(output, "ERR", 4);
        return;
    }

    for (; length > 0; --length, outLength -= 2)
    {
        unsigned char byte = *buff++;

        *output++ = binHex[(byte >> 4) & 0x0F];
        *output++ = binHex[byte & 0x0F];
    }
    if (outLength-- <= 0) return;
    *output++ = '\0';
}

1
使用 MVP 建议的更改,我创建了此函数,其中包括错误检查(无效字符和不均匀长度)。
该函数将把具有偶数个字符的十六进制字符串(未以 "0x" 开头)转换为指定字节数的数字。如果遇到无效字符或十六进制字符串长度为奇数,则返回 -1;成功时返回 0。
//convert hexstring to len bytes of data
//returns 0 on success, -1 on error
//data is a buffer of at least len bytes
//hexstring is upper or lower case hexadecimal, NOT prepended with "0x"
int hex2data(unsigned char *data, const unsigned char *hexstring, unsigned int len)
{
    unsigned const char *pos = hexstring;
    char *endptr;
    size_t count = 0;

    if ((hexstring[0] == '\0') || (strlen(hexstring) % 2)) {
        //hexstring contains no data
        //or hexstring has an odd length
        return -1;
    }

    for(count = 0; count < len; count++) {
        char buf[5] = {'0', 'x', pos[0], pos[1], 0};
        data[count] = strtol(buf, &endptr, 0);
        pos += 2 * sizeof(char);

        if (endptr[0] != '\0') {
            //non-hexadecimal character encountered
            return -1;
        }
    }

    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接