【问题标题】:Convert hex string (char []) to int?将十六进制字符串(char [])转换为int?
【发布时间】:2020-07-18 08:33:12
【问题描述】:

我有一个包含诸如“0x1800785”之类的值的 char[],但我想赋予该值的函数需要一个 int,如何将其转换为 int?我四处寻找,但找不到答案。谢谢。

【问题讨论】:

标签: c char int


【解决方案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】:

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

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

【讨论】:

  • 对于微控制器等资源有限的平台非常有用。
【解决方案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】:

假设你的意思是它是一个字符串,strtol 怎么样?

【讨论】:

  • 我最初链接到 strtod (-> double) 而不是 strtol。我猜有人在我编辑时看到了它。
  • 嗯,这不是什么大错...如果将返回值提供给int,编译器会自动转换。 +1,顺便说一句。
  • 我不认为 double 可以存储 32 位整数可以存储的所有可能值(实际上,有人知道这是不是真的吗?我不是 100% 的浮点数表示。)
  • @JamesMcLaughlin 可以。 64 位 IEEE double 的积分精度约为 2^53。
  • 详细信息,“64 位 IEEE double 的积分精度约为 2^53。”和一个符号位。所以它可以处理int54_tuint53_t
【解决方案5】:

如果您有可用的 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 &lt; 16; i++) { c = *(str + i); v = (c &amp; 0xF) + (c &gt;&gt; 6) | ((c &gt;&gt; 3) &amp; 0x8); n = (n &lt;&lt; 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 &gt;&gt; 6) | ((c &gt;&gt; 3) &amp; 0x8) → 在字母的情况下计算为 9,在小数的情况下计算为 0。是的,非常hacky: )
【解决方案6】:

所以,经过一段时间的搜索,发现 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

【讨论】:

    【解决方案7】:

    一个快速而肮脏的解决方案:

    // 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 的帖子,很可爱
    【解决方案8】:

    试试下面的代码块,它对我有用。

    char p[] = "0x820";
    uint16_t intVal;
    sscanf(p, "%x", &intVal);
    
    printf("value x: %x - %d", intVal, intVal);
    

    输出是:

    value x: 820 - 2080
    

    【讨论】:

      【解决方案9】:

      这是一个直接将包含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;
      }
      

      【讨论】:

        【解决方案10】:

        我做过类似的事情,认为它可能对你有帮助 它实际上对我有用

        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' 来给出它们等效的十六进制值,我没有这样做,因为我不需要它。

        【讨论】:

          【解决方案11】:

          我做了一个库来进行十六进制/十进制转换,而不使用stdio.h。使用非常简单:

          unsigned hexdec (const char *hex, const int s_hex);
          

          在第一次转换之前初始化用于转换的数组:

          void init_hexdec ();
          

          这里是github上的链接:https://github.com/kevmuret/libhex/

          【讨论】:

            【解决方案12】:

            我喜欢@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;
            

            不受格式错误的字符串保护。

            【讨论】:

              【解决方案13】:

              这是一个基于“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
              }
              
              

              【讨论】:

                【解决方案14】:

                我知道这确实很旧,但我认为解决方案看起来太复杂了。在 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 
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-11-27
                  • 2019-01-02
                  • 2023-03-08
                  相关资源
                  最近更新 更多