Преобразовать шестнадцатеричную строку в целое число в C эффективно?

в C, каков наиболее эффективный способ преобразования строки шестнадцатеричных цифр в двоичный unsigned int или unsigned long?

например, если у меня есть 0xFFFFFFFE, Я хочу int со значением base10 4294967294.

15 ответов


вы хотите strtol или strtoul. См. также Unix man page


Edit:теперь совместим с компиляторами MSVC, C++ и non-GNU (см. Конец).

вопрос был "наиболее эффективным способом."ОП не указывает платформу, он может компилироваться для чипа ATMEL на основе RISC с 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
};

попробуйте это:

#include <stdio.h>
int main()
{
    char s[] = "fffffffe";
    int x;
    sscanf(s, "%x", &x);
    printf("%u\n", x);
}

Если у вас нет stdlib, вам нужно сделать это вручную.

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 или 64bits, и нет ошибки захвата для незаконных шестнадцатеричных символов.


как будто часто случается, ваш вопрос страдает от серьезной терминологической ошибки / двусмысленности. В обычной речи это обычно не имеет значения, но в контексте этой конкретной проблемы это критически важно.

вы видите, нет такой вещи, как" шестнадцатеричное значение "и" десятичное значение "(или" шестнадцатеричное число "и"десятичное число"). "Hex" и "decimal" являются свойствами представления ценностей. Между тем, значения (или числа) сами по себе не имеют представления, поэтому они не может быть "шестнадцатеричным"или " десятичным". Например, 0xF и 15 в синтаксисе C два разных представления of такое же количество.

Я бы предположил, что ваш вопрос, как он сформулирован, предполагает, что вам нужно преобразовать шестнадцатеричное представление ASCII значения (т. е. строку) в десятичное представление ASCII значения (другую строку). Один из способов сделать это-использовать целочисленное представление в качестве промежуточного: во-первых, преобразовать ASCII hex представление в целое число достаточного размера (с использованием функций из strto... группы, как strtol), затем преобразуйте целое число в десятичное представление ASCII (используя sprintf).

если это не то, что вам нужно сделать, то вы должны уточнить свой вопрос, так как невозможно понять это из того, как сформулирован ваш вопрос.


@Eric

Почему решение кода, которое работает, получает голос? Конечно, это уродливо и, возможно, не самый быстрый способ сделать это, но более поучительно, что говорят "strtol" или "sscanf". Если вы попробуете это сами, вы узнаете что-то о том, как все происходит под капотом.

Я действительно не думаю, что ваше решение должно было быть отклонено, но я предполагаю, почему это происходит, потому что это менее практично. Идея с голосованием заключается в том, что "лучший" ответ будет плавать к вершине, и хотя ваш ответ может быть более поучительным о том, что происходит под капотом (или как это может произойти), это определенно не лучший способ разбора шестнадцатеричных чисел в производственной системе.

опять же, я не думаю, что что-то не так с вашим ответом с образовательной точки зрения, и я, конечно, не стал бы (и не стал) голосовать за него. Не расстраивайтесь и не прекращайте публикацию только потому, что некоторым людям не понравился один из ваших ответов. Он происходит.

Я сомневаюсь, что мой ответ заставляет вас чувствовать себя лучше о вашем голосовании, но я знаю, что это особенно не весело, когда вы спрашиваете, почему что-то голосуют и никто не отвечает.


для больших шестнадцатеричных строк, как в примере, мне нужно использовать strtoul.


шестнадцатеричных в десятичные. Не запускайте его в онлайн-компиляторах, потому что он не будет работать.

#include<stdio.h>
void main()
{
    unsigned int i;
    scanf("%x",&i);
    printf("%d",i);
}

для микроконтроллеров AVR я написал следующую функцию, включая соответствующие комментарии, чтобы было легко понять:

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

будет выводить: enter image description here


почему решение кода, которое работает голосовали против? Конечно, это некрасиво ...

возможно, потому, что, будучи уродливым, он не является образовательным и не работа. Кроме того, я подозреваю, что, как и я, большинство людей в настоящее время не имеют возможности редактировать (и, судя по рангу, никогда не будут).

использование массива может быть хорошим для эффективности, но это не упоминается в этом коде. Он также не принимает во внимание Верхний и Нижний случай, поэтому он не работает для примера, приведенного в вопросе. FFFFFFFE


@Eric

Я на самом деле надеялся увидеть, что Мастер 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;
}

У меня изначально было больше битовой маски вместо сравнений, но я серьезно сомневаюсь, что битовая маска быстрее, чем сравнение на современном оборудовании.


попробуйте преобразовать из Decimal в Hex

    #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;
}

#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);
}

В 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;
}

в настоящее время это работает только с нижним регистром, но его очень легко заставить работать с обоими.

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";}
        else{cout << "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";