有没有标准的C函数可以将十六进制字符串转换为字节数组?
我不想自己编写函数。
#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;
}
正如 Al 指出的那样,在字符串中有奇数个十六进制数字时,你必须确保在其前面添加一个起始的 0。例如,字符串 "f00f5"
将被以上示例错误地解释为 {0xf0, 0x0f, 0x05}
,而不是正确的 {0x0f, 0x00, 0xf5}
。
稍微更改了示例以回应 @MassimoCallegari 的评论。
x
转换说明符适用于像strtoul
这样的字符串,它期望按照C11 6.4.4.1中定义的输入进行。 - Michael Foukarakis我通过谷歌搜索找到了这个问题。我不喜欢调用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);
}
idx0 = (uint8_t)str[pos+0] & 0x1F ^ 0x10;
对于 idx1 同样如此。然后你可以移除 01234567
行之前和 HIJKLMNO
行之后的所有字节。 - 5andr0sscanf
和strtol
可能有些过度,但是一个不必要的32行十六进制表却不是? - Jakobfor (pos = 0; ((pos < (blen*2)) && (pos < strlen(str))); pos += 2)
。 - pbn除了上面优秀的回答,我想写一个不使用任何库并且对错误字符串做了一些防护的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;
}
说明:
a. index / 2 | 整数相除会向下取整,所以0/2=0,1/2=0,2/2=1,3/2=1,4/2=2,5/2=2等。因此,对于每2个字符串字符,我们将该值添加到1个数据字节中。
b. (index + 1) % 2 | 我们希望奇数结果为1,偶数为0,因为十六进制字符串的第一个数字是最重要的数字,并且需要乘以16。所以对于索引0 => 0 + 1 % 2 = 1,索引1 => 1 + 1 % 2 = 0等。
c. << 4 | 左移4位相当于乘以16。例如:b00000001 << 4 = b00010000
if(slength % 2 == 0)
应该是 if(slength % 2 != 0)
。否则似乎工作正常。 - sudodata
)的指针!这不是 C 风格,让用户提供缓冲区。 - IMAN4K对于短字符串,strtol
、strtoll
和strtoimax
可以正常工作(请注意第三个参数是处理字符串时使用的进制...将其设置为16)。 如果您的输入超过 最长整数类型字位数/4
,则需要使用其他答案建议的更灵活的方法之一。
#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);
}
}
}
通过对用户411313代码的一些修改,以下内容适用于我:
#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;
}
int i; ... uint8_t str_len
都应该改为 size_t
。 - chux - Reinstate Monicachar *hexstring = "de ad be ef 10 20 30 40 b0 0b 1e 50";
,它可以正常工作。太好了!编辑:但我们应该调整循环。 - ollydbg2312
可以被 str_len
替换,也许只需要使用字符数组来节省空间? - LinconFive你可以使用这个函数,它是为了性能而设计的(适用于嵌入式处理器),没有scanf
、strtol
或动态内存分配
/// in: valid chars are 0-9 + A-F + a-f
/// out_len_max==0: convert until the end of input string, out_len_max>0 only convert this many numbers
/// returns actual out size
int hexStr2Arr(unsigned char* out, const char* in, size_t out_len_max = 0)
{
if (!out_len_max)
out_len_max = 2147483647; // INT_MAX
const int in_len = strnlen(in, out_len_max * 2);
if (in_len % 2 != 0)
return -1; // error, in str len should be even
// calc actual out len
const int out_len = out_len_max < (in_len / 2) ? out_len_max : (in_len / 2);
for (int i = 0; i < out_len; i++) {
char ch0 = in[2 * i];
char ch1 = in[2 * i + 1];
uint8_t nib0 = (ch0 & 0xF) + (ch0 >> 6) | ((ch0 >> 3) & 0x8);
uint8_t nib1 = (ch1 & 0xF) + (ch1 >> 6) | ((ch1 >> 3) & 0x8);
out[i] = (nib0 << 4) | nib1;
}
return out_len;
}
使用说明:
unsigned char result[128];
memset(result, 0, 128); // optional
printf("result len=%d\n", hexStr2Arr(result, "0a0B10")); // result = [0A 0B 10 00 00 ...]
memset(result, 0, 128); // optional
// only convert single number
printf("result len=%d\n", hexStr2Arr(result, "0a0B10", 1)); // result = [0A 00 00 00 00 ...]
这里有相对清晰易读的十六进制转二进制和二进制转十六进制代码。(请注意,原本通过错误日志系统返回的是枚举错误代码,而不是简单的-1或-2.)
typedef unsigned char ByteData;
ByteData HexChar (char c)
{
if ('0' <= c && c <= '9') return (ByteData)(c - '0');
if ('A' <= c && c <= 'F') return (ByteData)(c - 'A' + 10);
if ('a' <= c && c <= 'f') return (ByteData)(c - 'a' + 10);
return (ByteData)(-1);
}
ssize_t HexToBin (const char* s, ByteData * buff, ssize_t length)
{
ssize_t result = 0;
if (!s || !buff || length <= 0) return -2;
while (*s)
{
ByteData nib1 = HexChar(*s++);
if ((signed)nib1 < 0) return -3;
ByteData nib2 = HexChar(*s++);
if ((signed)nib2 < 0) return -4;
ByteData bin = (nib1 << 4) + nib2;
if (length-- <= 0) return -5;
*buff++ = bin;
++result;
}
return result;
}
void BinToHex (const ByteData * buff, ssize_t length, char * output, ssize_t outLength)
{
char binHex[] = "0123456789ABCDEF";
if (!output || outLength < 4) return (void)(-6);
*output = '\0';
if (!buff || length <= 0 || outLength <= 2 * length)
{
memcpy(output, "ERR", 4);
return (void)(-7);
}
for (; length > 0; --length, outLength -= 2)
{
ByteData byte = *buff++;
*output++ = binHex[(byte >> 4) & 0x0F];
*output++ = binHex[byte & 0x0F];
}
if (outLength-- <= 0) return (void)(-8);
*output++ = '\0';
}
以下是我为了性能而编写的解决方案:
void hex2bin(const char* in, size_t len, unsigned char* out) {
static const unsigned char TBL[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 58, 59, 60, 61,
62, 63, 64, 10, 11, 12, 13, 14, 15, 71, 72, 73, 74, 75,
76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
90, 91, 92, 93, 94, 95, 96, 10, 11, 12, 13, 14, 15
};
static const unsigned char *LOOKUP = TBL - 48;
const char* end = in + len;
while(in < end) *(out++) = LOOKUP[*(in++)] << 4 | LOOKUP[*(in++)];
}
例子:
unsigned char seckey[32];
hex2bin("351aaaec0070d13d350afae2bc43b68c7e590268889869dde489f2f7988f3fee", 64, seckey);
/*
seckey = {
53, 26, 170, 236, 0, 112, 209, 61, 53, 10, 250, 226, 188, 67, 182, 140,
126, 89, 2, 104, 136, 152, 105, 221, 228, 137, 242, 247, 152, 143, 63, 238
};
*/
static const unsigned char TBL[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 58, 59,
60, 61, 62, 63, 64, 10, 11, 12, 13, 14, 15
};
hextools.h
#ifndef HEX_TOOLS_H
#define HEX_TOOLS_H
char *bin2hex(unsigned char*, int);
unsigned char *hex2bin(const char*);
#endif // HEX_TOOLS_H
hextools.c
#include <stdlib.h>
char *bin2hex(unsigned char *p, int len)
{
char *hex = malloc(((2*len) + 1));
char *r = hex;
while(len && p)
{
(*r) = ((*p) & 0xF0) >> 4;
(*r) = ((*r) <= 9 ? '0' + (*r) : 'A' - 10 + (*r));
r++;
(*r) = ((*p) & 0x0F);
(*r) = ((*r) <= 9 ? '0' + (*r) : 'A' - 10 + (*r));
r++;
p++;
len--;
}
*r = '\0';
return hex;
}
unsigned char *hex2bin(const char *str)
{
int len, h;
unsigned char *result, *err, *p, c;
err = malloc(1);
*err = 0;
if (!str)
return err;
if (!*str)
return err;
len = 0;
p = (unsigned char*) str;
while (*p++)
len++;
result = malloc((len/2)+1);
h = !(len%2) * 4;
p = result;
*p = 0;
c = *str;
while(c)
{
if(('0' <= c) && (c <= '9'))
*p += (c - '0') << h;
else if(('A' <= c) && (c <= 'F'))
*p += (c - 'A' + 10) << h;
else if(('a' <= c) && (c <= 'f'))
*p += (c - 'a' + 10) << h;
else
return err;
str++;
c = *str;
if (h)
h = 0;
else
{
h = 4;
p++;
*p = 0;
}
}
return result;
}
main.c
#include <stdio.h>
#include "hextools.h"
int main(void)
{
unsigned char s[] = { 0xa0, 0xf9, 0xc3, 0xde, 0x44 };
char *hex = bin2hex(s, sizeof s);
puts(hex);
unsigned char *bin;
bin = hex2bin(hex);
puts(bin2hex(bin, 5));
size_t k;
for(k=0; k<5; k++)
printf("%02X", bin[k]);
putchar('\n');
return 0;
}