Эффективно преобразовать шестнадцатеричную строку в целое число в C? - PullRequest
22 голосов
/ 14 августа 2008

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

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

Ответы [ 15 ]

39 голосов
/ 14 августа 2008

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

31 голосов
/ 17 июня 2012

Редактировать: Теперь совместимо с компиляторами 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 ++, которые не примут необычное шестнадцатеричное объявление.

Заменить первое утверждение этой (более длинной, но более соответствующей) версией:

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
};
15 голосов
/ 14 августа 2008

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

#include <stdio.h>
int main()
{
    char s[] = "fffffffe";
    int x;
    sscanf(s, "%x", &x);
    printf("%u\n", x);
}
7 голосов
/ 25 сентября 2008

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

5 голосов
/ 20 августа 2016

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

4 голосов
/ 29 октября 2010

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

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

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

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

2 голосов
/ 27 ноября 2011

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

#include<stdio.h>
void main()
{
    unsigned int i;
    scanf("%x",&i);
    printf("%d",i);
}
2 голосов
/ 14 августа 2008

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

2 голосов
/ 14 августа 2008

@ Эрик

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

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

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

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

1 голос
/ 04 августа 2012
#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);
}
...