C ++ mengonversi string hex ke integer yang ditandatangani

135

Saya ingin mengonversi string heks menjadi integer bertanda 32 bit di C ++.

Jadi, misalnya, saya punya string hex "fffefffe". Representasi biner dari ini adalah 11111111111111101111111111111110. Representasi integer yang ditandatangani adalah: -65538.

Bagaimana saya melakukan konversi ini di C ++? Ini juga perlu berfungsi untuk angka non-negatif. Misalnya, string hex "0000000A", yaitu 000000000000000000000000000000101010 dalam biner, dan 10 dalam desimal.

Clayton
sumber
2
Catatan. Anda hanya akan mendapatkan -65538 untuk sistem di mana sizeof (int) == 4
Martin York
3
@ Martin York, Dia tidak menyebutkan int. "32 bit integer ditandatangani" bisa int32_t atau __int32 dll.
Kirill V. Lyadvinsky

Jawaban:

230

menggunakan std::stringstream

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

contoh berikut menghasilkan -65538sebagai hasilnya:

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

Dalam standar C ++ 11 yang baru, ada beberapa fungsi utilitas baru yang dapat Anda manfaatkan! khusus, ada keluarga fungsi "string ke nomor" ( http://en.cppreference.com/w/cpp/string/basic_string/stol dan http://en.cppreference.com/w/cpp/string/ basic_string / stoul ). Ini pada dasarnya adalah pembungkus tipis di sekitar string C ke fungsi konversi angka, tetapi tahu bagaimana menangani astd::string

Jadi, jawaban paling sederhana untuk kode yang lebih baru mungkin akan terlihat seperti ini:

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

CATATAN: Di bawah ini adalah jawaban asli saya, yang seperti yang diedit mengatakan bukan jawaban yang lengkap. Untuk solusi fungsional, tempel kode di atas baris :-).

Tampaknya sejak lexical_cast<>didefinisikan memiliki semantik konversi aliran. Sayangnya, stream tidak mengerti notasi "0x". Jadi boost::lexical_casttangan saya yang digulung tidak cocok dengan string hex. Solusi di atas yang secara manual mengatur input stream ke hex akan menanganinya dengan baik.

Boost juga memiliki beberapa hal untuk dilakukan, yang juga memiliki beberapa kemampuan pengecekan kesalahan yang bagus. Anda bisa menggunakannya seperti ini:

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

Jika Anda merasa tidak ingin menggunakan boost, inilah versi ringan dari pemeran leksikal yang tidak memeriksa kesalahan:

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

yang dapat Anda gunakan seperti ini:

// though this needs the 0x prefix so it knows it is hex
unsigned int x = lexical_cast<unsigned int>("0xdeadbeef"); 
Evan Teran
sumber
1
Ketika saya menggunakan metode itu, saya berakhir dengan nilai integer dari 152144602
Clayton
@ jmanning2k, ya, itu aneh bahwa keduanya meningkatkan dan muntah lexical_cast saya pada string hex (bahkan dengan awalan 0x) jika saya tidak meletakkan std :: hex dalam string.
Evan Teran
1
@SteveWilkinson: Baca paragraf yang dimulai dengan " EDIT ". Ini menjelaskan bagaimana Anda perlu menggunakanstd::hex
Evan Teran
1
Untuk stringstreamsatu orang harus memeriksa ss.good() && ss.eof()untuk memastikan tidak ada kesalahan terjadi.
atoMerz
1
"busuk" ini menyelamatkan logika saya
vincenzopalazzo
60

Untuk metode yang bekerja dengan C dan C ++, Anda mungkin ingin mempertimbangkan untuk menggunakan strtol fungsi pustaka standar ().

#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;
    }
}
Geof Sawaya
sumber
2
Anda sebaiknya strtoultidak menggunakannya strtol. Akan ada + overflowsaat menggunakan strtol. Dengan strtoultidak akan ada overflow dan nilai yang dikembalikan akan dikonversi ke longuntuk menghasilkan hasil yang benar (-65538). Jadi jawaban Anda hampir benar :)
Kirill V. Lyadvinsky
8
+1. Karena strtol (atau strtoul) lebih cepat daripada menggunakan stringstream.
Kirill V. Lyadvinsky
27

Andy Buchanan, sejauh menempel pada C ++, saya menyukai milik Anda, tetapi saya memiliki beberapa mod:

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

Digunakan seperti

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

Dengan begitu Anda tidak perlu satu impl per tipe int.

Mike Lundy
sumber
1
Saya akan mengambil langkah itu juga, tetapi saya menemukan bahwa saya ingin membatasi proliferasi kurung sudut. Untuk kasus ini saya merasa melanggar aturan "jangan duplikat kode" dibenarkan. :-)
Andy J Buchanan
Sayangnya itu perlu, tetapi dilakukan dengan baik. Ditambahkan ke header STL / Boost ekstensi / perbaikan pribadi saya. Terima kasih!
Tim Sylvester
Sayangnya ini hanya berfungsi untuk konversi yang tidak ditandatangani. Jadi, Anda tidak dapat mengonversi 0xFFFFFFFF ke -1.
fmuecke
@ fmuecke: Itu karena 0xFFFFFFFF adalah overflow integer yang ditandatangani, yang merupakan perilaku tidak terdefinisi.
Billy ONeal
15

Contoh kerja dengan strtouladalah:

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

strtoldikonversi stringke long. Di komputer saya numeric_limits<long>::max()memberi 0x7fffffff. Jelas itu 0xfffefffelebih besar dari 0x7fffffff. Jadi strtolmengembalikan MAX_LONGbukan nilai yang diinginkan. strtoulmengkonversi stringke unsigned longitu sebabnya tidak ada kelebihan dalam hal ini.

Ok, strtolsedang mempertimbangkan string input bukan sebagai integer bertanda 32-bit sebelum konversi. Sampel lucu dengan 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;
    }
}

Kode di atas dicetak -65538di konsol.

Kirill V. Lyadvinsky
sumber
9

Inilah metode sederhana dan kerja yang saya temukan di tempat lain:

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

Harap dicatat bahwa Anda mungkin lebih suka menggunakan bilangan bulat panjang / bilangan bulat panjang, untuk menerima nilai. Catatan lain, fungsi c_str () baru saja mengubah std :: string menjadi const char *.

Jadi, jika Anda memiliki const char * siap, langsung saja menggunakan nama variabel itu secara langsung, seperti yang ditunjukkan di bawah ini [Saya juga menunjukkan penggunaan variabel panjang yang tidak ditandai untuk nomor hex yang lebih besar. Jangan bingung dengan kasus memiliki const char * bukannya 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);

Ini berfungsi baik-baik saja (asalkan Anda menggunakan tipe data yang sesuai sesuai kebutuhan Anda).

AamodG
sumber
6

Saya memiliki masalah yang sama hari ini, inilah cara saya menyelesaikannya sehingga saya dapat menyimpan 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...

(Menemukan halaman ini ketika saya sedang mencari cara yang kurang sial :-)

Ceria, A.

Andy J Buchanan
sumber
1
Kode memiliki kesalahan kompilasi sepele - nilai lebih tidak ditentukan (harus lebih dari nilai).
Gabi Davar
3

Ini bekerja untuk saya:

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 
mike
sumber
0

Coba ini. Solusi ini agak berisiko. Tidak ada cek. String hanya harus memiliki nilai hex dan panjang string harus cocok dengan ukuran tipe pengembalian. Tetapi tidak perlu header ekstra.

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

Pemakaian:

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

Catatan: panjang string harus dapat dibagi 2

Johannes Beutel
sumber