C ++ konwertuje ciąg szesnastkowy na liczbę całkowitą ze znakiem

135

Chcę przekonwertować ciąg szesnastkowy na 32-bitową liczbę całkowitą ze znakiem w C ++.

Na przykład mam ciąg szesnastkowy „fffefffe”. Binarna reprezentacja tego to 11111111111111101111111111111110. Reprezentacja liczb całkowitych ze znakiem to: -65538.

Jak wykonać tę konwersję w C ++? To również musi działać dla liczb nieujemnych. Na przykład ciąg szesnastkowy „0000000A”, czyli 00000000000000000000000000001010 w systemie dwójkowym i 10 w systemie dziesiętnym.

Clayton
źródło
2
Uwaga. Otrzymasz tylko -65538 dla systemów, w których sizeof (int) == 4
Martin York
3
@ Martin York, On nie wspomniał int. „32-bitowa liczba całkowita ze znakiem” może mieć postać int32_t lub __int32 itd.
Kirill V. Lyadvinsky

Odpowiedzi:

230

posługiwać się std::stringstream

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

następujący przykład daje -65538wynik:

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

W nowym standardzie C ++ 11 jest kilka nowych funkcji narzędziowych, z których możesz skorzystać! w szczególności istnieje rodzina funkcji „ciąg do numeru” ( http://en.cppreference.com/w/cpp/string/basic_string/stol i http://en.cppreference.com/w/cpp/string/ basic_string / stoul ). Są to zasadniczo cienkie opakowania wokół funkcji konwersji ciągów na liczby w języku C, ale wiedzą, jak sobie radzić zstd::string

Tak więc najprostsza odpowiedź na nowszy kod prawdopodobnie wyglądałaby następująco:

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

UWAGA: Poniżej znajduje się moja oryginalna odpowiedź, która zgodnie z edycją nie jest odpowiedzią pełną. Aby uzyskać funkcjonalne rozwiązanie, umieść kod powyżej linii :-).

Wydaje się, że ponieważ lexical_cast<>jest zdefiniowany jako semantyka konwersji strumienia. Niestety, strumienie nie rozumieją notacji „0x”. Więc zarówno ta, jak boost::lexical_casti moja wyrzucona ręka nie radzą sobie dobrze z ciągami hex. Powyższe rozwiązanie, które ręcznie ustawia strumień wejściowy na hex, poradzi sobie z tym dobrze.

Boost ma też kilka rzeczy do zrobienia, które ma również niezłe możliwości sprawdzania błędów. Możesz go używać w ten sposób:

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

Jeśli nie masz ochoty korzystać z funkcji Boost, oto lekka wersja rzutowania leksykalnego, która nie sprawdza błędów:

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

którego możesz użyć w ten sposób:

// though this needs the 0x prefix so it knows it is hex
unsigned int x = lexical_cast<unsigned int>("0xdeadbeef"); 
Evan Teran
źródło
1
Kiedy używam tej metody, otrzymuję liczbę całkowitą 152144602
Clayton
@ jmanning2k, tak, to dziwne, że zarówno boost, jak i mój lexical_cast barf na ciągach szesnastkowych (nawet z prefiksem 0x), jeśli nie wstawię std :: hex w ciągu.
Evan Teran
1
@SteveWilkinson: Przeczytaj akapit zaczynający się od „ EDYTUJ ”. Wyjaśnia, jak należy używaćstd::hex
Evan Teran,
1
Dla stringstreamS należy sprawdzić ss.good() && ss.eof(), aby upewnić się, że nie wystąpiły żadne błędy.
atoMerz
1
ten "stoul" ratuje moją logikę
vincenzopalazzo
60

W przypadku metody, która działa zarówno z C, jak i C ++, warto rozważyć użycie funkcji biblioteki standardowej 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;
    }
}
Geof Sawaya
źródło
2
Należy używać strtoulnie strtol. Będzie + overflowpodczas używania strtol. Dzięki temu strtoulnie będzie przepełnienia, a zwrócona wartość zostanie przekonwertowana na longpoprawny wynik (-65538). Więc twoja odpowiedź prawie poprawna :)
Kirill V. Lyadvinsky
8
+1. Ponieważ strtol (lub strtoul) szybciej niż przy użyciu stringstream.
Kirill V. Lyadvinsky
27

Andy Buchanan, jeśli chodzi o trzymanie się C ++, podobał mi się twój, ale mam kilka modów:

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

Używane jak

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

W ten sposób nie potrzebujesz jednego elementu impl na typ int.

Mike Lundy
źródło
1
Zrobiłbym też ten krok, ale uważam, że lubię ograniczać mnożenie się nawiasów kątowych. W tym przypadku uznałem, że złamanie zasady „nie powielaj kodu” jest uzasadnione. :-)
Andy J Buchanan
Szkoda, że ​​jest to konieczne, ale ładnie zrobione. Dodano do mojego osobistego nagłówka rozszerzeń / poprawek STL / Boost. Dzięki!
Tim Sylvester
Niestety działa to tylko w przypadku konwersji bez znaku. Więc nie możesz przekonwertować 0xFFFFFFFF na -1.
fmuecke
@fmuecke: Dzieje się tak, ponieważ 0xFFFFFFFF jest przepełnieniem liczby całkowitej ze znakiem, co jest niezdefiniowanym zachowaniem.
Billy ONeal
15

Przykład roboczy z strtoulbędzie:

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

strtolkonwertuje stringdo long. Na moim komputerze numeric_limits<long>::max()daje 0x7fffffff. Oczywiście to 0xfffefffejest większe niż 0x7fffffff. Więc strtolzwraca MAX_LONGzamiast pożądanej wartości. strtoulkonwertuje stringdounsigned long to, dlaczego w tym przypadku nie ma przepełnienia.

Ok, strtolrozważa wejściowy ciąg znaków nie jako 32-bitową liczbę całkowitą ze znakiem przed konwersją. Zabawna próbka zawierająca 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;
    }
}

Powyższy kod jest drukowany -65538w konsoli.

Kirill V. Lyadvinsky
źródło
9

Oto prosta i działająca metoda, którą znalazłem gdzie indziej:

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

Pamiętaj, że możesz preferować użycie długiej liczby całkowitej bez znaku / długiej liczby całkowitej do otrzymania wartości. Inna uwaga, funkcja c_str () po prostu konwertuje std :: string na const char *.

Więc jeśli masz gotową const char *, po prostu użyj bezpośrednio tej nazwy zmiennej, jak pokazano poniżej [Pokazuję również użycie długiej zmiennej bez znaku dla większej liczby szesnastkowej. Nie myl tego z przypadkiem posiadania const char * zamiast 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);

Działa to doskonale (pod warunkiem, że używasz odpowiednich typów danych zgodnie z potrzebami).

AamodG
źródło
6

Miałem dzisiaj ten sam problem, oto jak go rozwiązałem, aby zachować 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...

(Znalazłem tę stronę, gdy szukałem mniej pechowego sposobu :-)

Pozdrawiam, A.

Andy J Buchanan
źródło
1
Kod zawiera trywialny błąd kompilacji - wartość wyjściowa nie jest zdefiniowana (powinna być wartością outValue).
Gabi Davar
3

To zadziałało dla mnie:

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 
mikrofon
źródło
0

Spróbuj tego. Takie rozwiązanie jest nieco ryzykowne. Nie ma czeków. Ciąg musi mieć tylko wartości szesnastkowe, a długość ciągu musi odpowiadać rozmiarowi zwracanego typu. Ale nie ma potrzeby stosowania dodatkowych nagłówków.

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

Stosowanie:

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

Uwaga: długość łańcucha musi być podzielna przez 2

Johannes Beutel
źródło