在C中有效地将十六进制字符串转换为整数?

Posted

技术标签:

【中文标题】在C中有效地将十六进制字符串转换为整数?【英文标题】:Convert a hexadecimal string to an integer efficiently in C? 【发布时间】:2010-09-05 20:04:20 【问题描述】:

在 C 中,将一串十六进制数字转换为二进制 unsigned intunsigned long 的最有效方法是什么?

例如,如果我有 0xFFFFFFFE,我想要一个带有 base10 值 4294967294int

【问题讨论】:

【参考方案1】:

您想要strtolstrtoul。另见Unix man page

【讨论】:

【参考方案2】:

试试这个:

#include <stdio.h>
int main()

    char s[] = "fffffffe";
    int x;
    sscanf(s, "%x", &x);
    printf("%u\n", x);

【讨论】:

太棒了。我以前从未见过这种方法。【参考方案3】:

@埃里克

为什么有效的代码解决方案会被否决?当然,这很丑陋,可能不是最快的方法,但说“strtol”或“sscanf”更有指导意义。如果您自己尝试一下,您将了解事情是如何在幕后发生的。

我真的不认为你的解决方案应该被否决,但我猜测它为什么会发生是因为它不太实用。投票的想法是“最佳”答案将浮到顶部,虽然您的答案可能对幕后发生的事情(或可能发生的方式)更具指导性,但这绝对不是解析十六进制数字的最佳方式在生产系统中。

再一次,从教育的角度来看,我认为您的回答没有任何问题,我当然不会(也不会)投反对票。不要因为有些人不喜欢您的某个答案而气馁并停止发帖。它发生了。

我怀疑我的回答会让你对自己的被否决感觉更好,但我知道当你问为什么某事被否决和no one answers 时,这尤其不好玩。

【讨论】:

2008 年 8 月网站是全新的,cmets 未实施【参考方案4】:

@埃里克

我实际上希望看到一个 C 向导发布一些非常酷的东西,有点像我所做的,但不那么冗长,同时仍然“手动”进行。

好吧,我不是 C 大师,但这是我想出的:

unsigned int parseHex(const char * str)

    unsigned int val = 0;
    char c;

    while(c = *str++)
    
        val <<= 4;

        if (c >= '0' && c <= '9')
        
            val += c & 0x0F;
            continue;
        

        c &= 0xDF;
        if (c >= 'A' && c <= 'F')
        
            val += (c & 0x07) + 9;
            continue;
        

        errno = EINVAL;
        return 0;
    

    return val;

我最初进行了更多位掩码而不是比较,但我严重怀疑位掩码是否比现代硬件上的比较快。

【讨论】:

四个抱怨:1)它不编译。 2) Id 不处理小写 3) 它不起作用 (A => 1)。 4) 无效字符将被忽略!。你测试了吗? 你读了吗? “我实际上并没有编译这个,所以我可能会犯一些相当大的错误。”所以不,我没有测试它。 你去。我修补了它。作为记录,它已经通过“c &= 0xDF”语句处理了小写。不过,它以多种其他方式被破坏了。 第五个抱怨:如果您使用 ANSI C 编程(并且不保证具有基于 ASCII 的执行字符集),则无法保证 'A' + 1 == 'B'('a' &amp; 0xDF) == ('A' &amp; 0xDF)【参考方案5】:

对于示例中的较大十六进制字符串,我需要使用strtoul。

【讨论】:

【参考方案6】:

为什么代码解决方案有效 被否决?当然丑 ...

也许是因为除了丑陋之外,它没有教育意义并且没有工作。另外,我怀疑和我一样,大多数人目前都没有编辑的权力(从所需的等级来看 - 永远不会)。

使用数组可以提高效率,但是这段代码中没有提到。 它也没有考虑大小写,因此它不适用于问题中提供的示例。呸呸呸

【讨论】:

【参考方案7】:

如果您没有标准库,那么您必须手动完成。

unsigned long hex2int(char *a, unsigned int len)

    int i;
    unsigned long val = 0;

    for(i=0;i<len;i++)
       if(a[i] <= 57)
        val += (a[i]-48)*(1<<(4*(len-1-i)));
       else
        val += (a[i]-55)*(1<<(4*(len-1-i)));

    return val;

注意:此代码假定大写 A-F。如果 len 超出您的最长整数 32 位或 64 位,则它不起作用,并且不存在非法十六进制字符的错误捕获。

【讨论】:

a[i]-'0'a[i]-'A'+10 在您的系统使用 EBCDIC 的极少数情况下也可以工作(它们仍然存在)。 '0''A' 还可以让您的代码自我记录,以供不记住 ASCII 表的人使用。【参考方案8】:

这目前只适用于小写,但它非常容易同时适用于两者。

cout << "\nEnter a hexadecimal number: ";
cin >> hexNumber;
orighex = hexNumber;

strlength = hexNumber.length();

for (i=0;i<strlength;i++)

    hexa = hexNumber.substr(i,1);
    if ((hexa>="0") && (hexa<="9"))
    
        //cout << "This is a numerical value.\n";
    
    else
    
        //cout << "This is a alpabetical value.\n";
        if (hexa=="a")hexa="10";
        else if (hexa=="b")hexa="11";
        else if (hexa=="c")hexa="12";
        else if (hexa=="d")hexa="13";
        else if (hexa=="e")hexa="14";
        else if (hexa=="f")hexa="15";
        elsecout << "INVALID ENTRY! ANSWER WONT BE CORRECT\n";
    
    //convert from string to integer

    hx = atoi(hexa.c_str());
    finalhex = finalhex + (hx*pow(16.0,strlength-i-1));

cout << "The hexadecimal number: " << orighex << " is " << finalhex << " in decimal.\n";

【讨论】:

那是C++,他问的是C【参考方案9】:

就像经常发生的那样,您的问题存在严重的术语错误/歧义。在普通话中,它通常无关紧要,但在这个特定问题的背景下,它是至关重要的。

您看,没有“十六进制值”和“十进制值”(或“十六进制数”和“十进制数”)之类的东西。 “十六进制”和“十进制”是值的表示的属性。同时,值(或数字)本身没有表示形式,因此它们不能是“十六进制”或“十进制”。例如,C 语法中的0xF15同一个数的两个不同表示

我猜你的问题,它的陈述方式,表明你需要将一个值(即一个字符串)的 ASCII 十六进制表示转换为一个值(另一个字符串)的 ASCII 十进制表示。一种方法是使用整数表示作为中间表示:首先,将 ASCII 十六进制表示转换为足够大小的整数(使用 strto... 组中的函数,如 strtol),然后将整数转换为 ASCII十进制表示(使用sprintf)。

如果这不是你需要做的,那么你必须澄清你的问题,因为从你的问题提出的方式中是不可能弄清楚的。

【讨论】:

我也将问题阅读为十六进制字符串->十进制字符串,但这与其他答案不匹配。我编辑了问题以匹配接受的答案和大多数其他答案。 string->string 问题很模糊,但让我想知道是否可以在不通过二进制整数作为中间步骤的情况下完成它(例如,对于太大而无法放入 uint64_t 的数字)。但是,添加一串十进制数字很糟糕,所以可能不是。【参考方案10】:

试试这个把十进制转换成十六进制

    #include<stdio.h>
    #include<conio.h>

    int main(void)
    
      int count=0,digit,n,i=0;
      int hex[5];
      clrscr();
      printf("enter a number   ");
      scanf("%d",&n);

      if(n<10)
      
          printf("%d",n);
      

      switch(n)
      
          case 10:
              printf("A");
            break;
          case 11:
              printf("B");
            break;
          case 12:
              printf("B");
            break;
          case 13:
              printf("C");
            break;
          case 14:
              printf("D");
            break;
          case 15:
              printf("E");
            break;
          case 16:
              printf("F");
            break;
          default:;
       

       while(n>16)
       
          digit=n%16;
          hex[i]=digit;
          i++;
          count++;
          n=n/16;
       

       hex[i]=n;

       for(i=count;i>=0;i--)
       
          switch(hex[i])
          
             case 10:
                 printf("A");
               break;
             case 11:
                 printf("B");
               break;
             case 12:
                 printf("C");
               break;
             case  13:
                 printf("D");
               break;
             case 14:
                 printf("E");
               break;
             case 15:
                 printf("F");
               break;
             default:
                 printf("%d",hex[i]);
          
    

    getch();

    return 0;

【讨论】:

考虑将void main() 更改为int main(void) 十进制->十六进制更容易:您可以使用表格查找将 4 位整数转换为十六进制数字,而无需巨大的 switchchar hextable[] = '0', '1', ..., 'A', 'B', ..., 'F' ; 你可以用putchar 代替printf!此外,您的第一个开关有一个错误:"B" 用于 11 和 12,这就是为什么 16-> "F"。 /掌脸【参考方案11】:

十六进制转十进制。不要在在线编译器上运行它,因为它不起作用。

#include<stdio.h>
void main()

    unsigned int i;
    scanf("%x",&i);
    printf("%d",i);

【讨论】:

dis 可以在大写和小写两种情况下工作......我自己检查过它可以工作......【参考方案12】:

编辑:现在兼容 MSVC、C++ 和非 GNU 编译器(见末尾)。

问题是“最有效的方法”。 OP 没有指定平台,他可能正在编译基于 RISC 的 ATMEL 芯片,并为他的代码提供 256 字节的闪存。

为了记录,对于那些(像我一样)欣赏“最简单的方法”和“最有效的方法”之间的区别,并且喜欢学习的人......

static const long hextable[] = 
   [0 ... 255] = -1, // bit aligned access into this table is considerably
   ['0'] = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // faster for most modern processors,
   ['A'] = 10, 11, 12, 13, 14, 15,       // for the space conscious, reduce to
   ['a'] = 10, 11, 12, 13, 14, 15        // signed char.
;

/** 
 * @brief convert a hexidecimal string to a signed long
 * will not produce or process negative numbers except 
 * to signal error.
 * 
 * @param hex without decoration, case insensitive. 
 * 
 * @return -1 on error, or result (max (sizeof(long)*8)-1 bits)
 */
long hexdec(unsigned const char *hex) 
   long ret = 0; 
   while (*hex && ret >= 0) 
      ret = (ret << 4) | hextable[*hex++];
   
   return ret; 

它不需要外部库,而且速度应该非常快。它处理大写、小写、无效字符、奇数大小的十六进制输入(例如:0xfff),最大大小仅受编译器限制。

适用于非 GCC 或 C++ 编译器或不接受花哨的 hextable 声明的编译器。

用这个(更长,但更符合)版本替换第一个语句:

static const long hextable[] =  
    -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,-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,-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,-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
;

【讨论】:

我认为hextable 初始化代码是伪代码是正确的(如果是这样的话,值得指出),或者这是我不熟悉的一些深奥的数组初始化语法?跨度> 它不是用 android ndk-build 编译的。 @hB0 我将通过实物回复来回应那个令人难以置信的模糊和毫无意义的观察:它在 clang 上编译得很好。有 22 个警告,但这是意料之中的。 我在 android ndk - developer.android.com/tools/sdk/ndk/index.html 中使用了 ndk-build 工具,但它无法编译,特别是在数组声明上给了我错误。虽然我喜欢代码片段但我不能使用它,所以不得不使用其他好的方法(但效率低下)。现在不能给你确切的编译错误..(上次已经给你+1了) @hB0 只需用“[0..255]”注释掉第二行代码,并祈祷您永远不会传递无效输入【参考方案13】:
#include "math.h"
#include "stdio.h"
///////////////////////////////////////////////////////////////
//  The bits arg represents the bit say:8,16,32...                                                                                                              
/////////////////////////////////////////////////////////////
volatile long Hex_To_Int(long Hex,char bits)

    long Hex_2_Int;
    char byte;
    Hex_2_Int=0;

    for(byte=0;byte<bits;byte++)
    
        if(Hex&(0x0001<<byte))
            Hex_2_Int+=1*(pow(2,byte));
        else
            Hex_2_Int+=0*(pow(2,byte));
    

    return Hex_2_Int;

///////////////////////////////////////////////////////////////
//                                                                                                                  
/////////////////////////////////////////////////////////////

void main (void)

    int Dec;   
    char Hex=0xFA;
    Dec= Hex_To_Int(Hex,8);  //convert an 8-bis hexadecimal value to a number in base 10
    printf("the number is %d",Dec);

【讨论】:

代码将十六进制转换为十进制...没有复杂的编码..简单但有效。 天哪,这可能是我见过的最糟糕的十六进制到十进制实现。 pow 认真的吗?知道它通常被实现为pow(a,b) = exp( b * log(a) )。但即使不是,将整数转换为双精度已经是一项繁重的操作,尤其是在现代处理器上。 请注意,Hex_To_Int 将其输入作为存储在 long 中的 base-2 位串。十六进制字符串到整数的转换发生在编译时! “转换”只是一个非常昂贵的空操作,可以写成return Hex【参考方案14】:

对于 AVR 微控制器,我编写了以下函数,包括相关的 cmets 以使其易于理解:

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

例子:

char *z ="82ABC1EF";
uint32_t x = hex2int(z);
printf("Number is [%X]\n", x);

将输出:

【讨论】:

我不这么认为,但也许你忘了提供一些论据。【参考方案15】:

在 C 中,您可以通过多种方式将十六进制数转换为十进制数。一种方法是将十六进制数转换为整数。我个人觉得这既简单又小。

这是一个示例代码,用于在强制转换的帮助下将十六进制数转换为十进制数。

#include <stdio.h>

int main()
    unsigned char Hexadecimal = 0x6D;   //example hex number
    int Decimal = 0;    //decimal number initialized to 0


        Decimal = (int) Hexadecimal;  //conversion

    printf("The decimal number is %d\n", Decimal);  //output
    return 0;

【讨论】:

一个十六进制数字的字符串【参考方案16】:

如前所述,效率基本上取决于优化对象。

当优化代码行,或者只是在没有配备齐全的标准库的环境中工作时,一个快速而肮脏的选择可能是:

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

...更多类似的帖子在这里:https://***.com/a/58253380/5951263

【讨论】:

以上是关于在C中有效地将十六进制字符串转换为整数?的主要内容,如果未能解决你的问题,请参考以下文章

c语言编一函数将一十进制整数转换为十六进制整数,谢谢

C语言:怎么将十进制数字转换为二进制字符串,谁能帮我写个函数

如何将二进制整数转换为十六进制字符串?

如何递归地将十六进制转换为十进制[关闭]

C ++将十六进制字符串转换为有符号整数

16进制字符串如何转化16进制数值