Сведения о вопросе

PASHA

14:49, 8th August, 2020

Теги

c   performance   hex   strtol    

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

Просмотров: 751   Ответов: 15

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

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



  Сведения об ответе

LAST

03:47, 24th August, 2020

Вы хотите strtol или strtoul . Смотрите также man-страницу Unix


  Сведения об ответе

piter

08:04, 9th August, 2020

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

Вопрос был в том, что "most efficient way." OP не указывает платформу, он мог бы компилировать для чипа RISC на основе ATMEL с 256 байтами памяти flash для своего кода.

Для протокола, а также для тех (таких как я), кто ценит разницу между "the easiest way" и "самым эффективным способом", и кому нравится учиться...

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

Он не требует никаких внешних библиотек, и это должно быть ослепительно быстро. Он обрабатывает прописные, строчные, недопустимые символы, нечетные входные данные hex (например, 0xfff), а максимальный размер ограничен только компилятором.

Для не-GCC или C++ компиляторов или компиляторов, которые не будут принимать объявление fancy 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
};


  Сведения об ответе

PIRLO

19:30, 16th August, 2020

Попробовать это:

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


  Сведения об ответе

COOL

02:45, 3rd August, 2020

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


  Сведения об ответе

P_S_S

10:37, 12th August, 2020

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


  Сведения об ответе

$DOLLAR

02:34, 9th August, 2020

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

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

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

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


  Сведения об ответе

PIRLO

08:19, 6th August, 2020

@Eric

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

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

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

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


  Сведения об ответе

PAGE

18:55, 9th August, 2020

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


  Сведения об ответе

dumai

01:24, 10th August, 2020

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

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


  Сведения об ответе

piter

20:13, 25th August, 2020

Попробуйте это сделать, чтобы преобразовать из десятичной в 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;
}


  Сведения об ответе

darknet

08:18, 1st August, 2020

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

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

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


  Сведения об ответе

prince

01:53, 13th August, 2020

@Eric

Я на самом деле надеялся увидеть, как мастер C опубликует что-то действительно классное, вроде того, что я сделал, но менее многословно, все еще делая это "manually".

Ну, я не гуру 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;
}

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


  Сведения об ответе

PROGA

23:32, 23rd August, 2020

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


  Сведения об ответе

KOMP

10:50, 20th August, 2020

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


  Сведения об ответе

PIRLO

04:00, 14th August, 2020

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

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться