C ++ konvertiert Hex-String in vorzeichenbehaftete Ganzzahl

135

Ich möchte eine Hex-Zeichenfolge in eine 32-Bit-Ganzzahl mit Vorzeichen in C ++ konvertieren.

So habe ich zum Beispiel die Hex-Zeichenfolge "fffefffe". Die binäre Darstellung hierfür lautet 111111111111111011111111111110. Die vorzeichenbehaftete Ganzzahldarstellung hierfür lautet: -65538.

Wie mache ich diese Konvertierung in C ++? Dies muss auch für nicht negative Zahlen funktionieren. Zum Beispiel die Hex-Zeichenfolge "0000000A", die 00000000000000000000000000001010 in binär und 10 in dezimal ist.

Clayton
quelle
2
Hinweis. Sie erhalten nur -65538 für Systeme, bei denen sizeof (int) == 4
Martin York
3
@ Martin York, hat er nicht erwähnt int. "32-Bit-Ganzzahl mit Vorzeichen" könnte int32_t oder __int32 usw. sein.
Kirill V. Lyadvinsky

Antworten:

230

verwenden std::stringstream

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

Das folgende Beispiel ergibt -65538das Ergebnis:

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

Im neuen C ++ 11-Standard gibt es einige neue Dienstprogrammfunktionen, die Sie verwenden können! Insbesondere gibt es eine Familie von "String to Number" -Funktionen ( http://en.cppreference.com/w/cpp/string/basic_string/stol und http://en.cppreference.com/w/cpp/string/). basic_string / stoul ). Dies sind im Wesentlichen dünne Wrapper um Cs Funktionen zur Konvertierung von Zeichenfolgen in Zahlen, die jedoch wissen, wie man mit a umgehtstd::string

Die einfachste Antwort für neueren Code würde also wahrscheinlich so aussehen:

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

HINWEIS: Unten ist meine ursprüngliche Antwort, die, wie in der Bearbeitung angegeben, keine vollständige Antwort ist. Für eine funktionale Lösung kleben Sie den Code über die Zeile :-).

Es scheint, dass da lexical_cast<>eine Stream-Konvertierungssemantik definiert ist. Leider verstehen Streams die "0x" -Notation nicht. Also gehen sowohl die boost::lexical_castals auch meine Hand gerollt nicht gut mit Hex-Saiten um. Die obige Lösung, bei der der Eingabestream manuell auf hex gesetzt wird, funktioniert einwandfrei.

Boost hat auch einige Dinge zu tun, die auch einige nette Funktionen zur Fehlerprüfung bieten. Sie können es so verwenden:

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

Wenn Sie keine Lust auf Boost haben, finden Sie hier eine leichte Version der lexikalischen Besetzung, die keine Fehlerprüfung durchführt:

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

die Sie so verwenden können:

// though this needs the 0x prefix so it knows it is hex
unsigned int x = lexical_cast<unsigned int>("0xdeadbeef"); 
Evan Teran
quelle
1
Wenn ich diese Methode verwende, erhalte ich einen ganzzahligen Wert von 152144602
Clayton
@ jmanning2k, ja, es ist seltsam, dass sowohl boost als auch mein lexical_cast barf auf Hex-Strings (auch mit dem 0x-Präfix), wenn ich std :: hex nicht in den String setze.
Evan Teran
1
@SteveWilkinson: Lesen Sie den Absatz beginnend mit " EDIT ". Es erklärt, wie Sie verwenden müssenstd::hex
Evan Teran
1
Für stringstreams sollte man überprüfen ss.good() && ss.eof(), ob keine Fehler aufgetreten sind.
AtoMerz
1
Dieses "Stoul" rettet meine Logik
Vincenzopalazzo
60

Für eine Methode, die sowohl mit C als auch mit C ++ funktioniert, sollten Sie die Standardbibliotheksfunktion strtol () verwenden.

#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
quelle
2
Sie sollten strtoulnicht verwenden strtol. Es wird + overflowbei der Verwendung von strtol sein. Mit strtoulgibt es keinen Überlauf und der zurückgegebene Wert wird konvertiert long, um ein korrektes Ergebnis zu erzielen (-65538). Also deine Antwort fast richtig :)
Kirill V. Lyadvinsky
8
+1. Weil strtol (oder strtoul) schneller ist als stringstream.
Kirill V. Lyadvinsky
27

Andy Buchanan, was das Festhalten an C ++ angeht, hat mir deins gefallen, aber ich habe ein paar Mods:

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

Gebraucht wie

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

Auf diese Weise benötigen Sie kein impl pro int-Typ.

Mike Lundy
quelle
1
Ich hätte diesen Schritt auch getan, aber ich finde, dass ich die Verbreitung von spitzen Klammern gerne begrenzen möchte. In diesem Fall hielt ich es für gerechtfertigt, die Regel "Code nicht duplizieren" zu brechen. :-)
Andy J Buchanan
Schade, dass es notwendig ist, aber gut gemacht. Zu meinem persönlichen STL / Boost-Erweiterungs- / Fixes-Header hinzugefügt. Vielen Dank!
Tim Sylvester
Leider funktioniert dies nur bei nicht signierter Konvertierung. Sie können also 0xFFFFFFFF nicht in -1 konvertieren.
Fmuecke
@fmuecke: Das liegt daran, dass 0xFFFFFFFF ein vorzeichenbehafteter Ganzzahlüberlauf ist, der ein undefiniertes Verhalten darstellt.
Billy ONeal
15

Arbeitsbeispiel mit strtoulwird sein:

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

strtolkonvertiert stringzu long. Auf meinem Computer numeric_limits<long>::max()gibt 0x7fffffff. Offensichtlich 0xfffefffeist das größer als 0x7fffffff. So strtolkehrt MAX_LONGstatt wollte Wert. strtoulkonvertiert stringzu unsigned longdeshalb kein Überlauf in diesem Fall.

Ok, strtolerwägt, dass die Eingabezeichenfolge vor der Konvertierung nicht als vorzeichenbehaftete 32-Bit-Ganzzahl verwendet wird. Lustige Probe mit 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;
    }
}

Der obige Code wird -65538in der Konsole gedruckt .

Kirill V. Lyadvinsky
quelle
9

Hier ist eine einfache und funktionierende Methode, die ich an anderer Stelle gefunden habe:

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

Bitte beachten Sie, dass Sie möglicherweise die Verwendung einer vorzeichenlosen langen Ganzzahl / langen Ganzzahl bevorzugen, um den Wert zu erhalten. Ein weiterer Hinweis: Die Funktion c_str () konvertiert nur den std :: string in const char *.

Wenn Sie also ein const char * bereit haben, verwenden Sie einfach diesen Variablennamen direkt, wie unten gezeigt [Ich zeige auch die Verwendung der vorzeichenlosen langen Variablen für eine größere Hex-Zahl. Verwechseln Sie es nicht mit dem Fall, dass const char * anstelle von string] verwendet wird:

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

Dies funktioniert einwandfrei (vorausgesetzt, Sie verwenden die für Sie erforderlichen Datentypen).

AamodG
quelle
6

Ich hatte heute das gleiche Problem. So habe ich es gelöst, damit ich lexical_cast <> behalten kann

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...

(Fand diese Seite, als ich nach einem weniger blöden Weg suchte :-)

Prost, A.

Andy J Buchanan
quelle
1
Code hat einen trivialen Kompilierungsfehler - outvalue ist nicht definiert (sollte outValue sein).
Gabi Davar
3

Das hat bei mir funktioniert:

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
quelle
0

Versuche dies. Diese Lösung ist etwas riskant. Es gibt keine Schecks. Die Zeichenfolge darf nur Hex-Werte haben und die Zeichenfolgenlänge muss mit der Größe des Rückgabetyps übereinstimmen. Es sind jedoch keine zusätzlichen Header erforderlich.

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

Verwendung:

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

Hinweis: Die Länge der Zeichenfolge muss durch 2 teilbar sein

Johannes Beutel
quelle