C ++ перетворити шістнадцяткову рядок у підписане ціле число


135

Я хочу перетворити шістнадцяткову рядок у 32-бітове ціле число, підписане в C ++.

Так, наприклад, у мене є шістнадцятковий рядок "fffefffe". Двійкове представлення цього є 11111111111111101111111111111110. Підписане ціле представлення цього: -65538.

Як зробити цю конверсію в C ++? Це також потрібно працювати для негативних чисел. Наприклад, шістнадцятковий рядок "0000000A", який є 00000000000000000000000000001010 у двійковій та 10 у десятковій.


2
Примітка. Ви отримаєте -65538 лише для систем, де sizeof (int) == 4
Martin York

3
@Martin York, Він не згадував 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 ). Це по суті тонкі обгортки навколо рядка С в функції перетворення чисел, але знають, як боротися з astd::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, так, дивно, що і прискорення, і мій lexical_cast barf на шістнадцяткових рядках (навіть із префіксом 0x), якщо я не ставлю std :: hex у рядку.
Еван Теран

1
@SteveWilkinson: Прочитайте абзац, починаючи з " редагування ". Це пояснює, як вам потрібно користуватисяstd::hex
Evan Teran

1
Для stringstreams слід перевірити ss.good() && ss.eof(), чи не виникло помилок.
atoMerz

1
цей "stoul" збереже мою логіку
vincenzopalazzo

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

Таким чином, вам не потрібен один імпл на тип int.


1
Я б теж зробив цей крок, але я вважаю, що мені подобається обмежувати поширення кутових дужок. У цьому випадку я відчував виправдання порушення правила "не дублювати код". :-)
Енді Дж. Бучанан,

Невдало, що це потрібно, але гарно зроблено. Додано до мого особистого заголовка розширень / виправлень STL / Boost. Дякую!
Тім Сильвестр

На жаль, це працює лише для непідписаного перетворення. Таким чином, ви не можете конвертувати 0xFFFFFFFF в -1.
fmuecke

@fmuecke: Це тому, що 0xFFFFFFFF - це підписане ціле число, яке є невизначеним поведінкою.
Біллі ONeal

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 :: в const char *.

Отже, якщо у вас готовий const char *, просто продовжуйте використовувати це ім’я безпосередньо, як показано нижче [Я також показую використання неподписаної довгої змінної для більшого шістнадцяткового числа. Не плутайте його з випадком того, що ви маєте const char * замість рядка]:

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
Код має тривіальну помилку компіляції - outvalue не визначається (має бути 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.