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


135

Я хочу преобразовать шестнадцатеричную строку в 32-битное целое число со знаком в C ++.

Так, например, у меня есть шестнадцатеричная строка "fffefffe". Двоичное представление этого является 11111111111111101111111111111110. Целочисленное представление со знаком этого: -65538.

Как мне сделать это преобразование в C ++? Это также должно работать для неотрицательных чисел. Например, шестнадцатеричная строка «0000000A», это 00000000000000000000000000001010 в двоичном формате и 10 в десятичном виде.


2
Заметка. Вы получите -65538 только для систем, где sizeof (int) == 4
Martin York,

3
@ Мартин Йорк, он не упомянул int. «32-разрядное целое число со знаком» может быть int32_t или __int32 и т. Д.
Кирилл В. Лядвинский

Ответы:


230

использование std::stringstream

unsigned int x;   
std::stringstream ss;
ss << std::hex << "fffefffe";
ss >> x;

следующий пример выдает в -65538качестве результата:

#include <sstream>
#include <iostream>

int main() {
    unsigned int x;   
    std::stringstream ss;
    ss << std::hex << "fffefffe";
    ss >> x;
    // output it as a signed type
    std::cout << static_cast<int>(x) << std::endl;
}

В новом стандарте C ++ 11 есть несколько новых служебных функций, которые вы можете использовать! в частности, существует семейство функций «строка в число» ( http://en.cppreference.com/w/cpp/string/basic_string/stol и http://en.cppreference.com/w/cpp/string/ basic_string / stoul ). По сути, это тонкие обертки вокруг функций преобразования строк в числа в Си, но они знают, как обращаться сstd::string

Итак, самый простой ответ для более нового кода, вероятно, будет выглядеть так:

std::string s = "0xfffefffe";
unsigned int x = std::stoul(s, nullptr, 16);

ПРИМЕЧАНИЕ. Ниже приведен мой первоначальный ответ, который, как говорит редакция, не является полным ответом. Для функционального решения вставьте код над строкой :-).

Похоже, что так lexical_cast<>как определено, чтобы иметь семантику преобразования потока. К сожалению, потоки не понимают нотацию «0x». Так что boost::lexical_castи моя, и моя рука свернуты не очень хорошо с шестнадцатеричными строками. Вышеупомянутое решение, которое вручную устанавливает входной поток в шестнадцатеричный формат, прекрасно с этим справится.

У Boost есть кое-что для этого, а также есть хорошие возможности проверки ошибок. Вы можете использовать это так:

try {
    unsigned int x = lexical_cast<int>("0x0badc0de");
} catch(bad_lexical_cast &) {
    // whatever you want to do...
}

Если вы не хотите использовать boost, вот облегченная версия лексического приведения, которая не проверяет ошибки:

template<typename T2, typename T1>
inline T2 lexical_cast(const T1 &in) {
    T2 out;
    std::stringstream ss;
    ss << in;
    ss >> out;
    return out;
}

который вы можете использовать так:

// though this needs the 0x prefix so it knows it is hex
unsigned int x = lexical_cast<unsigned int>("0xdeadbeef"); 

1
Когда я использую этот метод, я получаю целочисленное значение 152144602
Клейтон

@ jmanning2k, да, это странно, что и boost, и мой lexical_cast barf на шестнадцатеричных строках (даже с префиксом 0x), если я не помещаю std :: hex в строку.
Эван Теран

1
@ SteveWilkinson: прочитайте абзац, начинающийся с « РЕДАКТИРОВАТЬ ». Это объясняет, как вам нужно использоватьstd::hex
Эван Теран

1
Для stringstreams нужно проверить, ss.good() && ss.eof()чтобы убедиться, что никаких ошибок не произошло.
atoMerz

1
этот "stoul" сохрани мою логику
Винченцопалаццо

60

Для метода, который работает как с C, так и с C ++, вы можете рассмотреть возможность использования стандартной библиотечной функции strtol ().

#include <cstdlib>
#include <iostream>
using namespace std;

int main() {
    string s = "abcd";
    char * p;
    long n = strtol( s.c_str(), & p, 16 );
    if ( * p != 0 ) { //my bad edit was here
        cout << "not a number" << endl;
    }
    else {
        cout << n << endl;
    }
}

2
Вы должны использовать strtoulне strtol. Будет + overflowпри использовании strtol. С strtoulне будет переполнения, и возвращаемое значение будет преобразовано в longдля получения правильного результата (-65538). Так что ваш ответ почти правильный :)
Кирилл Васильевич Лядвинский

8
+1. Потому что strtol (или strtoul) быстрее, чем при использовании stringstream.
Кирилл Васильевич Лядвинский

27

Энди Бьюкенен, в том, что касается C ++, мне понравился твой, но у меня есть несколько модов:

template <typename ElemT>
struct HexTo {
    ElemT value;
    operator ElemT() const {return value;}
    friend std::istream& operator>>(std::istream& in, HexTo& out) {
        in >> std::hex >> out.value;
        return in;
    }
};

Используется как

uint32_t value = boost::lexical_cast<HexTo<uint32_t> >("0x2a");

Таким образом, вам не нужен один impl для типа int.


1
Я бы тоже сделал этот шаг, но считаю, что мне нравится ограничивать распространение угловых скобок. В этом случае я чувствовал, что нарушение правила «не дублировать код» было оправданным. :-)
Энди Дж Бьюкенен

К сожалению, это необходимо, но красиво сделано. Добавлен в мой личный заголовок расширений / исправлений STL / Boost. Спасибо!
Тим Сильвестр

К сожалению, это работает только для беззнакового преобразования. Таким образом, вы не можете конвертировать 0xFFFFFFFF в -1.
fmuecke

@fmuecke: Это потому, что 0xFFFFFFFF является целочисленным переполнением со знаком, что является неопределенным поведением.
Билли Онил

15

Рабочий пример с strtoul:

#include <cstdlib>
#include <iostream>
using namespace std;

int main() { 
    string s = "fffefffe";
    char * p;
    long n = strtoul( s.c_str(), & p, 16 ); 
    if ( * p != 0 ) {  
        cout << "not a number" << endl;
    }    else {  
        cout << n << endl;
    }
}

strtolпреобразует stringв long. На моем компьютере numeric_limits<long>::max()дает 0x7fffffff. Очевидно, что 0xfffefffeэто больше, чем 0x7fffffff. Так что strtolвозвращается MAX_LONGвместо требуемого значения. strtoulпревращается stringвunsigned long , что нет , почему нет переполнения в этом случае.

Хорошо, strtolрассматривает входную строку не как 32-разрядное целое число со знаком перед преобразованием. Смешной образец с strtol:

#include <cstdlib>
#include <iostream>
using namespace std;

int main() { 
    string s = "-0x10002";
    char * p;
    long n = strtol( s.c_str(), & p, 16 ); 
    if ( * p != 0 ) {  
        cout << "not a number" << endl;
    }    else {  
        cout << n << endl;
    }
}

Код выше печатается -65538в консоли.


9

Вот простой и рабочий метод, который я нашел в другом месте:

string hexString = "7FF";
int hexNumber;
sscanf(hexString.c_str(), "%x", &hexNumber);

Обратите внимание, что вы можете предпочесть использовать беззнаковое длинное целое / длинное целое, чтобы получить значение. Еще одно замечание: функция c_str () просто конвертирует std :: string в const char *.

Так что, если у вас есть готовый const char *, просто используйте это имя переменной напрямую, как показано ниже [Я также показываю использование переменной без знака long для большего шестнадцатеричного числа. Не путайте это со случаем наличия const char * вместо string]:

const char *hexString = "7FFEA5"; //Just to show the conversion of a bigger hex number
unsigned long hexNumber; //In case your hex number is going to be sufficiently big.
sscanf(hexString, "%x", &hexNumber);

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


6

У меня была та же проблема сегодня, вот как я решил ее, чтобы я мог сохранить lexical_cast <>

typedef unsigned int    uint32;
typedef signed int      int32;

class uint32_from_hex   // For use with boost::lexical_cast
{
    uint32 value;
public:
    operator uint32() const { return value; }
    friend std::istream& operator>>( std::istream& in, uint32_from_hex& outValue )
    {
        in >> std::hex >> outValue.value;
    }
};

class int32_from_hex   // For use with boost::lexical_cast
{
    uint32 value;
public:
    operator int32() const { return static_cast<int32>( value ); }
    friend std::istream& operator>>( std::istream& in, int32_from_hex& outValue )
    {
        in >> std::hex >> outvalue.value;
    }
};

uint32 material0 = lexical_cast<uint32_from_hex>( "0x4ad" );
uint32 material1 = lexical_cast<uint32_from_hex>( "4ad" );
uint32 material2 = lexical_cast<uint32>( "1197" );

int32 materialX = lexical_cast<int32_from_hex>( "0xfffefffe" );
int32 materialY = lexical_cast<int32_from_hex>( "fffefffe" );
// etc...

(Нашел эту страницу, когда искал менее удачный способ :-)

Ура, А.


1
В коде есть тривиальная ошибка компиляции - значение out не определено (должно быть outValue).
Габи Давар

3

Это сработало для меня:

string string_test = "80123456";
unsigned long x;
signed long val;

std::stringstream ss;
ss << std::hex << string_test;
ss >> x;
// ss >> val;  // if I try this val = 0
val = (signed long)x;  // However, if I cast the unsigned result I get val = 0x80123456 

0

Попробуй это. Это решение немного рискованно. Там нет чеков. Строка должна иметь только шестнадцатеричные значения, а длина строки должна соответствовать размеру возвращаемого типа. Но нет необходимости в дополнительных заголовках.

char hextob(char ch)
{
    if (ch >= '0' && ch <= '9') return ch - '0';
    if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
    if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
    return 0;
}
template<typename T>
T hextot(char* hex)
{
    T value = 0;
    for (size_t i = 0; i < sizeof(T)*2; ++i)
        value |= hextob(hex[i]) << (8*sizeof(T)-4*(i+1));
    return value;
};

Использование:

int main()
{
    char str[4] = {'f','f','f','f'};
    std::cout << hextot<int16_t>(str)  << "\n";
}

Примечание: длина строки должна делиться на 2

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.