C ++ converte una stringa esadecimale in intero con segno


135

Voglio convertire una stringa esadecimale in un intero con segno a 32 bit in C ++.

Quindi, per esempio, ho la stringa esadecimale "fffefffe". La rappresentazione binaria di questo è 11111111111111101111111111111110. La rappresentazione intera con segno di questo è: -65538.

Come posso fare questa conversione in C ++? Questo deve funzionare anche per numeri non negativi. Ad esempio, la stringa esadecimale "0000000A", che è 00000000000000000000000000001010 in binario e 10 in decimale.


2
Nota. Otterrai solo -65538 per i sistemi in cui sizeof (int) == 4
Martin York

3
@Martin York, non ha menzionato int. "Numero intero con segno a 32 bit" potrebbe essere int32_t o __int32 ecc.
Kirill V. Lyadvinsky,

Risposte:


230

uso std::stringstream

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

il seguente esempio produce -65538come risultato:

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

Nel nuovo standard C ++ 11, ci sono alcune nuove funzioni di utilità che puoi usare! in particolare, esiste una famiglia di funzioni "stringa a numero" ( http://en.cppreference.com/w/cpp/string/basic_string/stol e http://en.cppreference.com/w/cpp/string/ basic_string / stoul ). Si tratta essenzialmente di wrapper sottili attorno alle funzioni di conversione da stringa a numero di C, ma sanno come gestire astd::string

Quindi, la risposta più semplice per il codice più recente dovrebbe apparire così:

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

NOTA: Di seguito è la mia risposta originale, che come dice la modifica non è una risposta completa. Per una soluzione funzionale, inserisci il codice sopra la linea :-).

Sembra che da allora lexical_cast<>sia definito per avere la semantica della conversione del flusso. Purtroppo, i flussi non comprendono la notazione "0x". Quindi sia la boost::lexical_castmia che la mia mano arrotolata non si comportano bene con le stringhe esadecimali. La soluzione di cui sopra che imposta manualmente il flusso di input su esadecimale lo gestirà bene.

Boost ha anche alcune cose da fare, che ha anche delle buone capacità di controllo degli errori. Puoi usarlo in questo modo:

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

Se non hai voglia di usare boost, ecco una versione leggera del cast lessicale che non verifica errori:

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

che puoi usare in questo modo:

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

1
Quando uso quel metodo, finisco con un valore intero di 152144602
Clayton,

@ jmanning2k, sì, è strano che sia boost sia il mio lexical_cast barf su stringhe esadecimali (anche con il prefisso 0x) se non inserisco std :: hex nella stringa.
Evan Teran,

1
@SteveWilkinson: leggi il paragrafo che inizia con " EDIT ". Spiega come è necessario utilizzarestd::hex
Evan Teran il

1
Per stringstreamuno si dovrebbe verificare ss.good() && ss.eof()per assicurarsi che non si siano verificati errori.
atoMerz,

1
questo "stoul" salva la mia logica
vincenzopalazzo,

60

Per un metodo che funziona con C e C ++, potresti prendere in considerazione l'uso della funzione di libreria standard 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
Si dovrebbe usare strtoulnon strtol. Ci sarà + overflowquando si utilizza strtol. Con strtoulnon ci sarà overflow e il valore restituito verrà convertito longper produrre il risultato corretto (-65538). Quindi la tua risposta è quasi corretta :)
Kirill V. Lyadvinsky,

8
+1. Perché strtol (o strtoul) più veloce dell'uso di stringstream.
Kirill V. Lyadvinsky,

27

Andy Buchanan, per quanto riguarda il C ++, mi è piaciuto il tuo, ma ho alcune 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;
    }
};

Usato come

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

In questo modo non è necessario un impianto per tipo int.


1
Avrei fatto anche quel passo, ma trovo che mi piace limitare la proliferazione di parentesi angolari. In questo caso, ho ritenuto che infrangere la regola "non duplicare il codice" fosse giustificata. :-)
Andy J Buchanan,

Peccato che sia necessario, ma ben fatto. Aggiunto alla mia intestazione STL / Boost extension / fix personale. Grazie!
Tim Sylvester,

Purtroppo questo funziona solo per la conversione senza segno. Quindi non puoi convertire 0xFFFFFFFF in -1.
fmuecke,

@fmuecke: questo perché 0xFFFFFFFF è un overflow di numeri interi con segno, che è un comportamento indefinito.
Billy ONeal,

15

L'esempio di lavoro con strtoulsarà:

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

strtolconverte stringin long. Sul mio computer numeric_limits<long>::max()0x7fffffff. Ovviamente questo 0xfffefffeè maggiore di 0x7fffffff. Quindi strtolrestituisce MAX_LONGinvece del valore desiderato. strtoulsi converte stringin unsigned longquesto motivo per cui nessun overflow in questo caso.

Ok, strtolsta considerando che la stringa di input non è un intero con segno a 32 bit prima della conversione. Campione divertente con 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;
    }
}

Il codice sopra viene stampato -65538sulla console.


9

Ecco un metodo semplice e funzionante che ho trovato altrove:

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

Tieni presente che potresti preferire l'utilizzo di numeri interi lunghi / numeri lunghi senza segno per ricevere il valore. Un'altra nota, la funzione c_str () converte solo lo std :: string in const char *.

Quindi, se hai un const char * pronto, vai avanti usando direttamente quel nome di variabile, come mostrato sotto [Sto anche mostrando l'uso della variabile lunga senza segno per un numero esadecimale più grande. Non confonderlo con il caso di avere const char * invece di 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);

Funziona perfettamente (purché tu usi tipi di dati appropriati per le tue necessità).


6

Ho avuto lo stesso problema oggi, ecco come l'ho risolto in modo da poter mantenere 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...

(Ho trovato questa pagina mentre cercavo un modo meno schifoso :-)

Saluti, A.


1
Il codice ha un banale errore di compilazione - outvalue non è definito (dovrebbe essere outValue).
Gabi Davar,

3

Questo ha funzionato per me:

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

Prova questo. Questa soluzione è un po 'rischiosa. Non ci sono assegni. La stringa deve avere solo valori esadecimali e la lunghezza della stringa deve corrispondere alla dimensione del tipo restituito. Ma non c'è bisogno di intestazioni extra.

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

Uso:

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

Nota: la lunghezza della stringa deve essere divisibile per 2

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.