如何将十六进制字符串(char [])转换为整数?

105

我有一个char[]类型的值,例如 "0x1800785",但是我想将其赋值给需要int类型的函数,如何将其转换为int类型?我搜索了一下但找不到答案。谢谢。


1
可能是将字符串转换为有符号整数的重复问题。 - bitmask
1
使用 strtol() 函数,基数为 16,如何使用的其余部分在此处:https://dev59.com/TWYq5IYBdhLWcg3wzDzk - ivan.ukr
16个回答

255
你尝试过使用 strtol() 吗?

strtol - 将字符串转换为长整型数值

示例:

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,但我不建议引入冗余。)


5
如果十六进制字符串总是以问题中给出的 "0x" 开头,那么你应该使用 0 而不是 16 - Jens Gustedt
5
顺便提一下,strtol 函数返回 long 类型,处理非常大的十六进制数时非常有用。对于更大的十六进制数,请使用类型为 long longstrtoll 函数。 - Steven Lu
2
注意到一个令人惊讶的差异,即“以0x前缀开头时,必须使用0作为基数”,而C11 §7.22.1.4中指出:“如果基数的值为16,则字符0x或0X可以选择性地在字母和数字序列之前出现”,例如printf("%ld\n", strtol("0x12", 0, 16));。您可能需要重新审查此内容。 - chux - Reinstate Monica
@chux 将“必须”更改为“应该”,因为让函数检测基数(它消除了一点冗余)不是强制性的,但是更好的实践方法。 - user529758
1
从技术上讲,你应该使用strtoul而不是strtol,因为它将其转换为无符号值,这意味着您不会从8位十六进制值中获得负输出。这曾经困扰了我很长一段时间!请参见http://en.cppreference.com/w/c/string/byte/strtoul。 - Joseph238
显示剩余4条评论

32

或者,如果你想要拥有自己的实现,我写了这个快速函数作为示例:

/**
 * 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;
}

2
非常适用于资源有限的平台,如微控制器。 - Mubin Icyer

25

类似这样的东西可能会很有用:

char str[] = "0x1800785";
int num;

sscanf(str, "%x", &num);
printf("0x%x %i\n", num, num); 

阅读sscanf手册


2
这会导致未定义的行为,%x 必须接收一个 unsigned int 的地址。即使你修复了这个问题,如果字符串表示的数字大于 UINT_MAX,那么它仍然是未定义的行为。 - M.M

12

如果您有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; } - iamoumuamua

11

假设您的意思是它是一个字符串,那么怎么样使用strtol呢?


我最开始链接了strtod(-> double),而不是strtol。我猜在我编辑的时候有人看到了它。 - James M
好吧,这不算什么错误...如果返回值被传递给“int”,编译器会自动转换类型。顺便加1。 - user529758
我认为一个double类型无法存储所有32位整数可能的值(实际上,有人知道这是真的吗?我对浮点数表示并不完全确定)。 - James M
4
可以。64位IEEE双精度浮点数的整数精度大约为2的53次方。 - user166390
一个64位的IEEE双精度浮点数在整数精度上大约能达到2^53次方,此外还有一个符号位。因此它可以处理int54_tuint53_t - chux - Reinstate Monica

6
一个快速而简单的解决方案:
// 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)通过将除最后三位以外的位置清零来读取(有效地使其适用于大写或小写),然后减去1(因为在位掩码之后,字母从第一个位置开始),最后加上10(因为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;
}

1
回答完后,我发现错过了@LimeRed的帖子,那个很可爱。 - Simon

4

试一下以下代码块,它对我有效。

char p[] = "0x820";
uint16_t intVal;
sscanf(p, "%x", &intVal);

printf("value x: %x - %d", intVal, intVal);

输出结果为:

value x: 820 - 2080

3

经过一段时间的搜索后,我发现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是因为它的十六进制长度为8。

速度测试(1,000,000次调用):

strtol ~ 0.4400s
hexToInt ~ 0.1100s

1
我之前做过类似的事情,我认为这可能会对你有所帮助。
以下是我的解决方案:
 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'赋予它们对应的十六进制值。但是,我没有这样做,因为我不需要。


1
这是一个用于将包含十六进制的字符数组直接转换为整数的函数,不需要额外的库:
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;
}

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