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

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

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

Ответов (16)

Решение

Вы хотите strtolили strtoul. См. Также справочную страницу Unix

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

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

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

Вопрос был в «наиболее действенном способе». OP не указывает платформу, он может компилировать для чипа 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 "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;
}

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

При оптимизации для строк кода или просто при работе в среде без полностью оборудованной стандартной библиотеки одним быстрым и грязным вариантом может быть:

// 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://answacode.com/a/58253380/5951263

Для микроконтроллеров 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);

Выведет: введите описание изображения здесь

@ Эрик

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

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

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

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

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

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

@ Эрик

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

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

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

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

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

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

Если у вас нет 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;
}

Примечание. Этот код предполагает AF в верхнем регистре. Это не работает, если len превышает ваше самое длинное целое число 32 или 64 бита, и нет перехвата ошибок для недопустимых шестнадцатеричных символов.

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

Видите ли, не существует таких понятий, как «шестнадцатеричное значение» и «десятичное значение» (или «шестнадцатеричное число» и «десятичное число»). «Шестнадцатеричный» и «десятичный» - это свойства представлений значений. Между тем, значения (или числа) сами по себе не имеют представления, поэтому они не могут быть «шестнадцатеричными» или «десятичными». Например, 0xF и 15 в синтаксисе C два разных представления одного и того же числа .

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

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

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

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

Попробуйте это преобразовать из десятичного в шестнадцатеричный

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