当从十六进制表示中解码时,我需要将一个(可能非常长的)字符串(如char * s = "2f0a3f"
)转换为它所代表的实际字节。目前我正在这样做,但感觉笨重而且错误。
size_t hexlength = strlen(s);
size_t binlength = hexlength / 2;
unsigned char * buffer = malloc(binlength);
long i = 0;
char a, b;
for (; i < hexlength; i += 2) {
a = s[i + 0]; b = s[i + 1];
buffer[i / 2] =
((a < '9' ? a - '0' : a - 'a' + 10) << 4) + (b < '9' ? b - '0' : b - 'a' + 10);
}
有两件事让我感到丑陋:
有更好的方法吗?最好不要使用我必须添加依赖的东西(因为我希望以最小的跨平台问题发布此代码)。我的按位数学很糟糕;)
注意:数据已经过预先验证,全部为小写,并且是十六进制对的正确字符串。
答案 0 :(得分:6)
/* allocate the buffer */
char * buffer = malloc((strlen(s) / 2) + 1);
char *h = s; /* this will walk through the hex string */
char *b = buffer; /* point inside the buffer */
/* offset into this string is the numeric value */
char xlate[] = "0123456789abcdef";
for ( ; *h; h += 2, ++b) /* go by twos through the hex string */
*b = ((strchr(xlate, *h) - xlate) * 16) /* multiply leading digit by 16 */
+ ((strchr(xlate, *(h+1)) - xlate));
已编辑添加
在80x86汇编语言中,strchr()的核心基本上是一个指令 - 它不会循环。
另外:这没有边界检查,不能用于Unicode控制台输入,如果传递了无效字符就会崩溃。
同时:感谢那些指出一些严重错别字的人。
答案 1 :(得分:4)
并不是说它会产生很大的不同,但我会选择乘法除法。另外值得拆分数字代码,因为您可能希望将其移植到a-f
在字符集中不相邻的平台(仅开玩笑!)
inline int digittoint(char d) {
return ((d) <= '9' ? (d) - '0' : (d) - 'a' + 10);
}
#define digittoint(d) ((d) <= '9' ? (d) - '0' : (d) - 'a' + 10)
size_t hexlength = strlen(s);
size_t binlength = hexlength / 2;
unsigned char * buffer = malloc(binlength);
long i = 0;
char a, b;
for (; i < binlength; ++i) {
a = s[2 * i + 0]; b = s[2 * i + 1];
buffer[i] = (digittoint(a) << 4) | digittoint(b);
}
我修复了你的digit-to-int实现中的一个错误,并用bitwise取代了+
,或者说它更能表达你的意图。
然后,您可以尝试找到digittoint
的最佳实现 - 如上所述的条件算术,strspn
或查找表。
这是一个可能的无分支实现 - 奖金! - 适用于大写字母:
inline int digittoint(char d) {
return (d & 0x1f) + ((d >> 6) * 0x19) - 0x10;
}
答案 2 :(得分:1)
尝试这样的事情:
const unsigned char bin[128] =
{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
int hexlength = strlen(s);
int binlength = (hexlength / 2);
unsigned char * buffer = (unsigned char *) malloc(binlength);
if (buffer)
{
char *hex = s;
unsigned char *buf = buffer;
unsigned char b, c;
int ok = 1;
for (int i = 0; i < hexlength; i += 2)
{
b = bin[*hex++];
c = bin[*hex++];
if ((b == -1) || (c == -1))
{
ok = 0;
break;
)
*buf++ = ((b << 4) | c);
}
if (ok == 1)
{
// use buffer as needed, up to binlength number of bytes...
}
free(buffer);
}
答案 3 :(得分:0)
如果您需要将您的号码(以字符串形式)从十六进制转换为十进制,则可以将atol()
与sprintf()
如果你需要逐个字节地执行,你可以缓冲每个字节,并且当每个缓冲区被填充时,通过sprintf传递它:
char *hexRep;
char *decRep;
long int decVal;
...
decVal = atol(hexRep);
sprintf(decRep, "%u", decVal);
这两个都在C的标准库中。获得每个字节的字符串表示后,您可以将它们与strcat()
连接在一起。
答案 4 :(得分:0)
这里有一些小改进是MISRA的合规性。这个名字令人困惑。
static inline uint8_t HexcharToInt(char c) {
char result = 16;
if (('0' <= c) && (c <= '9')) {
result = c - '0';
} else if (('a' <= c) && (c <= 'f')) {
result = c + 10 - 'a';
} else if (('A' <= c) && (c <= 'F')) {
result = c + 10 - 'A';
}
return (uint8_t) result;
}
uint8_t *array = NULL;
size_t hexstringToArray(char *hexstring) {
size_t len = (strlen(hexstring) + 1) / 2; // Aufrunden
if (array != NULL) {
free(array);
array = NULL;
}
array = (uint8_t*) malloc(len);
uint8_t *arr = array;
for (size_t i = 0; (i < len) && (len > 0); i++) {
*arr = 0U;
for (uint8_t shift = 8U; (shift > 0U) && (len > 0); ) {
shift -= 4U;
uint8_t curInt = HexcharToInt(*hexstring++);
if (curInt >= 16U) {
len = 0;
} else {
*arr |= ((uint8_t) curInt << shift);
}
}
arr++;
}
return len;
}
答案 5 :(得分:0)
我想出了一个更简单的函数,它获取字符串并将转换结果逐字节复制到给定N大小的字节数组,并进行边界和完整性检查:
int8_t convert_str_to_bytes(uint8_t *byte_array, char* str, size_t n)
{
char *hex_match = "0123456789ABCDEF";
int i, j = 0;
char cbuf[3];
long ibuf;
if (strlen(str) < n) {
printf("ERROR: String is shorter than specified size.\n");
return -1;
}
for (i = 0; i < n; i += 2) {
strncpy(cbuf, &str[i], 2);
if (strspn(cbuf, hex_match) != 2) {
printf("ERROR: String is not a hexadecimal representation. Breaking now...\n");
return -1;
}
ibuf = strtol(cbuf, NULL, 16);
byte_array[j] = (uint8_t)ibuf;
++j;
}
return 0;
}
答案 6 :(得分:-1)
inline char HexToChar(char c)
{
if ('0' <= c && c <= '9')
{
return c - '0';
}
else if ('a' <= c && c <= 'f')
{
return c + 10 - 'a';
}
else if ('A' <= c && c <= 'F')
{
return c + 10 - 'A';
}
return -1;
}
size_t HexToBinrary( const char* hex, size_t length, char* binrary, size_t binrary_cap )
{
if (length % 2 != 0 || binrary_cap < length / 2)
{
return 0;
}
memset(binrary, 0, binrary_cap);
size_t n = 0;
for (size_t i = 0; i < length; i += 2, ++n)
{
char high = HexToChar(hex[i]);
if (high < 0)
{
return 0;
}
char low = HexToChar(hex[i + 1]);
if (low < 0)
{
return 0;
}
binrary[n] = high << 4 | low;
}
return n;
}