将十六进制字符串(char [])转换为int?
Posted
技术标签:
【中文标题】将十六进制字符串(char [])转换为int?【英文标题】:Convert hex string (char []) to int? 【发布时间】:2012-04-26 17:41:34 【问题描述】:我有一个包含诸如“0x1800785”之类的值的 char[],但我想赋予该值的函数需要一个 int,如何将其转换为 int?我四处寻找,但找不到答案。谢谢。
【问题讨论】:
convert string into signed int 的可能重复项 使用strtol(),base=16,其余的使用方法在这里:***.com/questions/14176123/correct-usage-of-strtol 【参考方案1】:你试过strtol()
吗?
strtol - convert string to a long integer
例子:
const char *hexstring = "abcdef0";
int number = (int)strtol(hexstring, NULL, 16);
如果数字的字符串表示以 0x
前缀开头,则 必须 应该使用 0 作为基数:
const char *hexstring = "0xabcdef0";
int number = (int)strtol(hexstring, NULL, 0);
(也可以指定一个明确的基数,例如 16,但我不建议引入冗余。)
【讨论】:
如果十六进制字符串总是由问题中给出的"0x"
引入,您应该只使用0
而不是16
。
作为旁注strtol
给你输入long
这在处理非常大的十六进制时非常有用。使用 strtoll
输入 long long
以获得更大的十六进制数。
注意到“以 0x 前缀开头,必须使用 0 作为基数”和 C11 §7.22.1.4 “如果基数的值为 16,则字符 0x 或 0X 可以可选地放在前面字母和数字的序列”,如printf("%ld\n", strtol("0x12", 0, 16));
。您不妨回顾一下。
@chux 将“必须”更改为“应该”,因为这不是强制性的,而是更好的做法,让函数检测碱基(它消除了一点点冗余)。
从技术上讲,您应该使用 strtoul 而不是 strtol,因为它将其转换为无符号数,这意味着您不会从 8 位十六进制值中得到负输出。这让我难倒了好一阵子!见en.cppreference.com/w/c/string/byte/strtoul【参考方案2】:
假设你的意思是它是一个字符串,那么strtol呢?
【讨论】:
我最初链接到 strtod (-> double) 而不是 strtol。我猜有人在我编辑时看到了它。 嗯,这不是什么大错...如果将返回值提供给int
,编译器会自动转换。 +1,顺便说一句。
我不认为 double 可以存储 32 位整数可以存储的所有可能值(实际上,有人知道这是不是真的吗?我不是 100% 的浮点数表示。)
@JamesMcLaughlin 可以。 64 位 IEEE double 的积分精度约为 2^53。
详细信息,“64 位 IEEE double 的积分精度约为 2^53。”和一个符号位。所以它可以处理int54_t
和uint53_t
。【参考方案3】:
这样的东西可能很有用:
char str[] = "0x1800785";
int num;
sscanf(str, "%x", &num);
printf("0x%x %i\n", num, num);
阅读man sscanf
【讨论】:
这会导致未定义的行为,%x
必须接收unsigned int
的地址。即使你解决了这个问题,如果字符串表示的数字大于UINT_MAX
,那么它又是未定义的行为【参考方案4】:
我做了一个库来进行十六进制/十进制转换,而不使用stdio.h
。使用非常简单:
unsigned hexdec (const char *hex, const int s_hex);
在第一次转换之前初始化用于转换的数组:
void init_hexdec ();
这里是github上的链接:https://github.com/kevmuret/libhex/
【讨论】:
【参考方案5】:试试下面的代码块,它对我有用。
char p[] = "0x820";
uint16_t intVal;
sscanf(p, "%x", &intVal);
printf("value x: %x - %d", intVal, intVal);
输出是:
value x: 820 - 2080
【讨论】:
【参考方案6】:我做过类似的事情,认为它可能对你有帮助 它实际上对我有用
int main()
int co[8];
char ch[8];
printf("please enter the string:");
scanf("%s", ch);
for (int i=0; i<=7; i++)
if ((ch[i]>='A') && (ch[i]<='F'))
co[i] = (unsigned int) ch[i]-'A'+10;
else if ((ch[i]>='0') && (ch[i]<='9'))
co[i] = (unsigned int) ch[i]-'0'+0;
这里我只取了一个 8 个字符的字符串。 如果你想要你可以为 'a' 添加类似的逻辑到 'f' 来给出它们等效的十六进制值,我没有这样做,因为我不需要它。
【讨论】:
【参考方案7】:或者如果你想有自己的实现,我写了这个快速函数作为例子:
/**
* hex2int
* take a hex string and convert it to a 32bit number (max 8 hex digits)
*/
uint32_t hex2int(char *hex)
uint32_t val = 0;
while (*hex)
// get current character then increment
uint8_t byte = *hex++;
// transform hex character to the 4bit equivalent number, using the ascii table indexes
if (byte >= '0' && byte <= '9') byte = byte - '0';
else if (byte >= 'a' && byte <='f') byte = byte - 'a' + 10;
else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10;
// shift 4 to make space for new digit, and add the 4 bits of the new digit
val = (val << 4) | (byte & 0xF);
return val;
【讨论】:
对于微控制器等资源有限的平台非常有用。【参考方案8】:所以,经过一段时间的搜索,发现 strtol 非常慢,我编写了自己的函数。它只适用于大写字母,但添加小写功能不是问题。
int hexToInt(PCHAR _hex, int offset = 0, int size = 6)
int _result = 0;
DWORD _resultPtr = reinterpret_cast<DWORD>(&_result);
for(int i=0;i<size;i+=2)
int _multiplierFirstValue = 0, _addonSecondValue = 0;
char _firstChar = _hex[offset + i];
if(_firstChar >= 0x30 && _firstChar <= 0x39)
_multiplierFirstValue = _firstChar - 0x30;
else if(_firstChar >= 0x41 && _firstChar <= 0x46)
_multiplierFirstValue = 10 + (_firstChar - 0x41);
char _secndChar = _hex[offset + i + 1];
if(_secndChar >= 0x30 && _secndChar <= 0x39)
_addonSecondValue = _secndChar - 0x30;
else if(_secndChar >= 0x41 && _secndChar <= 0x46)
_addonSecondValue = 10 + (_secndChar - 0x41);
*(BYTE *)(_resultPtr + (size / 2) - (i / 2) - 1) = (BYTE)(_multiplierFirstValue * 16 + _addonSecondValue);
return _result;
用法:
char *someHex = "#CCFF00FF";
int hexDevalue = hexToInt(someHex, 1, 8);
1 因为我们要转换的十六进制从偏移量 1 开始,而 8 因为它是十六进制长度。
速度测试(1.000.000 次调用):
strtol ~ 0.4400s
hexToInt ~ 0.1100s
【讨论】:
【参考方案9】:我知道这确实很旧,但我认为解决方案看起来太复杂了。在 VB 中试试这个:
Public Function HexToInt(sHEX as String) as long
Dim iLen as Integer
Dim i as Integer
Dim SumValue as Long
Dim iVal as long
Dim AscVal as long
iLen = Len(sHEX)
For i = 1 to Len(sHEX)
AscVal = Asc(UCase(Mid$(sHEX, i, 1)))
If AscVal >= 48 And AscVal <= 57 Then
iVal = AscVal - 48
ElseIf AscVal >= 65 And AscVal <= 70 Then
iVal = AscVal - 55
End If
SumValue = SumValue + iVal * 16 ^ (iLen- i)
Next i
HexToInt = SumValue
End Function
【讨论】:
【参考方案10】:如果您有可用的 libc,请使用 strtol
,如最佳答案所示。但是,如果您喜欢定制的东西或者在没有 libc 左右的微控制器上,您可能需要一个稍微优化的版本,而不需要复杂的分支。
#include <inttypes.h>
/**
* xtou64
* Take a hex string and convert it to a 64bit number (max 16 hex digits).
* The string must only contain digits and valid hex characters.
*/
uint64_t xtou64(const char *str)
uint64_t res = 0;
char c;
while ((c = *str++))
char v = (c & 0xF) + (c >> 6) | ((c >> 3) & 0x8);
res = (res << 4) | (uint64_t) v;
return res;
移位魔术归结为:只需使用最后 4 位,但如果是非数字,则还要加 9。
【讨论】:
如果有机会,您能否详细说明“v =”行的工作原理?我设法为自己的目的实现了您的功能,如下所示:unsigned long long int hextou64(char *str) unsigned long long int n = 0; char c, v; for (unsigned char i = 0; i < 16; i++) c = *(str + i); v = (c & 0xF) + (c >> 6) | ((c >> 3) & 0x8); n = (n << 4) | (unsigned long long int)v; return n;
让我看看我能不能把它放在一起^^我猜这很棘手..前提:'0'-'9'都以'0011??'开头在二进制。 'A'-'F' 都以 '0100????' 开头在二进制。我们可以使用此信息来避免分支。 (c & 0xF) → 只有最后四位包含该值。这已经是 '0'-'9' 的正确值。在“A”-“F”的情况下,我们必须添加一个小数点 9 以获得正确的值。 (例如十六进制 C 以 0011 结尾。它是 3。但我们想要 12。因此我们必须添加 9。(c >> 6) | ((c >> 3) & 0x8)
→ 在字母的情况下计算为 9,在小数的情况下计算为 0。是的,非常hacky: )【参考方案11】:
一个快速而肮脏的解决方案:
// makes a number from two ascii hexa characters
int ahex2int(char a, char b)
a = (a <= '9') ? a - '0' : (a & 0x7) + 9;
b = (b <= '9') ? b - '0' : (b & 0x7) + 9;
return (a << 4) + b;
您必须确保输入正确,不包括验证(可以说是 C)。好在它非常紧凑,它适用于“A”到“F”和“a”到“f”。
该方法依赖于字母字符在 ASCII 表中的位置,让我们看看例如*** (https://en.wikipedia.org/wiki/ASCII#/media/File:USASCII_code_chart.png)。长话短说,数字在字符下方,因此数字字符(0 到 9)很容易通过减去代码为零来转换。字母字符(A 到 F)通过除最后三位之外的零(有效地使其与大写或小写一起使用)、减一(因为在位掩码后,字母从位置一开始)和加十(因为 A 到 F 代表十六进制代码中的第 10 到第 15 个值)。最后,我们需要将构成编码数字的低半字节和高半字节的两位数字组合起来。
这里我们采用相同的方法(略有不同):
#include <stdio.h>
// takes a null-terminated string of hexa characters and tries to
// convert it to numbers
long ahex2num(unsigned char *in)
unsigned char *pin = in; // lets use pointer to loop through the string
long out = 0; // here we accumulate the result
while(*pin != 0)
out <<= 4; // we have one more input character, so
// we shift the accumulated interim-result one order up
out += (*pin < 'A') ? *pin & 0xF : (*pin & 0x7) + 9; // add the new nibble
pin++; // go ahead
return out;
// main function will test our conversion fn
int main(void)
unsigned char str[] = "1800785"; // no 0x prefix, please
long num;
num = ahex2num(str); // call the function
printf("Input: %s\n",str); // print input string
printf("Output: %x\n",num); // print the converted number back as hexa
printf("Check: %ld = %ld \n",num,0x1800785); // check the numeric values matches
return 0;
【讨论】:
发完答案后发现我错过了@LimeRed 的帖子,很可爱【参考方案12】:这是一个直接将包含char数组的十六进制转换为不需要额外库的整数的函数:
int hexadecimal2int(char *hdec)
int finalval = 0;
while (*hdec)
int onebyte = *hdec++;
if (onebyte >= '0' && onebyte <= '9')onebyte = onebyte - '0';
else if (onebyte >= 'a' && onebyte <='f') onebyte = onebyte - 'a' + 10;
else if (onebyte >= 'A' && onebyte <='F') onebyte = onebyte - 'A' + 10;
finalval = (finalval << 4) | (onebyte & 0xF);
finalval = finalval - 524288;
return finalval;
【讨论】:
【参考方案13】:我喜欢@radhoo 解决方案,它在小型系统上非常有效。可以修改将十六进制转换为 int32_t(因此是有符号值)的解决方案。
/**
* hex2int
* take a hex string and convert it to a 32bit number (max 8 hex digits)
*/
int32_t hex2int(char *hex)
uint32_t val = *hex > 56 ? 0xFFFFFFFF : 0;
while (*hex)
// get current character then increment
uint8_t byte = *hex++;
// transform hex character to the 4bit equivalent number, using the ascii table indexes
if (byte >= '0' && byte <= '9') byte = byte - '0';
else if (byte >= 'a' && byte <='f') byte = byte - 'a' + 10;
else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10;
// shift 4 to make space for new digit, and add the 4 bits of the new digit
val = (val << 4) | (byte & 0xF);
return val;
注意返回值是 int32_t 而 val 仍然是 uint32_t 不会溢出。
uint32_t val = *hex > 56 ? 0xFFFFFFFF : 0;
不受格式错误的字符串保护。
【讨论】:
【参考方案14】:这是一个基于“sairam singh”解决方案的解决方案。如果答案是一对一的解决方案,那么这个解决方案会将两个 ASCII 半字节组合成一个字节。
// Assumes input is null terminated string.
//
// IN OUT
// -------------------- --------------------
// Offset Hex ASCII Offset Hex
// 0 0x31 1 0 0x13
// 1 0x33 3
// 2 0x61 A 1 0xA0
// 3 0x30 0
// 4 0x00 NULL 2 NULL
int convert_ascii_hex_to_hex2(char *szBufOut, char *szBufIn)
int i = 0; // input buffer index
int j = 0; // output buffer index
char a_byte;
// Two hex digits are combined into one byte
while (0 != szBufIn[i])
// zero result
szBufOut[j] = 0;
// First hex digit
if ((szBufIn[i]>='A') && (szBufIn[i]<='F'))
a_byte = (unsigned int) szBufIn[i]-'A'+10;
else if ((szBufIn[i]>='a') && (szBufIn[i]<='f'))
a_byte = (unsigned int) szBufIn[i]-'a'+10;
else if ((szBufIn[i]>='0') && (szBufIn[i]<='9'))
a_byte = (unsigned int) szBufIn[i]-'0';
else
return -1; // error with first digit
szBufOut[j] = a_byte << 4;
// second hex digit
i++;
if ((szBufIn[i]>='A') && (szBufIn[i]<='F'))
a_byte = (unsigned int) szBufIn[i]-'A'+10;
else if ((szBufIn[i]>='a') && (szBufIn[i]<='f'))
a_byte = (unsigned int) szBufIn[i]-'a'+10;
else if ((szBufIn[i]>='0') && (szBufIn[i]<='9'))
a_byte = (unsigned int) szBufIn[i]-'0';
else
return -2; // error with second digit
szBufOut[j] |= a_byte;
i++;
j++;
szBufOut[j] = 0;
return 0; // normal exit
【讨论】:
以上是关于将十六进制字符串(char [])转换为int?的主要内容,如果未能解决你的问题,请参考以下文章