C ++ converter cadeia hexadecimal em número inteiro assinado

135

Eu quero converter uma seqüência hexadecimal em um inteiro assinado de 32 bits em C ++.

Então, por exemplo, eu tenho a string hexadecimal "fffefffe". A representação binária disso é 11111111111111101111111111111110. A representação inteira assinada disso é: -65538.

Como faço essa conversão em C ++? Isso também precisa funcionar para números não negativos. Por exemplo, a sequência hexadecimal "0000000A", que é 000000000000000000000000000000001010 em binário e 10 em decimal.

Clayton
fonte
2
Nota. Você só vai conseguir -65.538 para sistemas onde sizeof (int) == 4
Martin Iorque
3
@ Martin York, ele não mencionou int. "32 bit inteiro assinado" poderia ser int32_t ou __int32 etc.
Kirill V. Lyadvinsky

Respostas:

230

usar std::stringstream

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

o exemplo a seguir produz -65538como resultado:

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

No novo padrão C ++ 11, existem algumas novas funções utilitárias que você pode usar! especificamente, há uma família de funções "string to number" ( http://en.cppreference.com/w/cpp/string/basic_string/stol e http://en.cppreference.com/w/cpp/string/ basic_string / stoul ). Trata-se de invólucros essencialmente finos em torno das funções de conversão de string para número, mas sabem como lidar com umstd::string

Portanto, a resposta mais simples para o código mais recente provavelmente seria assim:

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

NOTA: Abaixo está a minha resposta original, que, como diz a edição, não é uma resposta completa. Para uma solução funcional, cole o código acima da linha :-).

Parece que desde lexical_cast<> está definido para ter semântica de conversão de fluxo. Infelizmente, os fluxos não entendem a notação "0x". Então, tanto a boost::lexical_castmão quanto a minha mão rolada não lidam bem com cordas hexagonais. A solução acima, que define manualmente o fluxo de entrada como hexadecimal, lidará perfeitamente com isso.

O Boost também tem algumas coisas para fazer isso, que também oferecem alguns recursos interessantes de verificação de erros. Você pode usá-lo assim:

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

Se você não gosta de usar o boost, aqui está uma versão leve do elenco lexical que não faz verificação de erro:

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

que você pode usar assim:

// though this needs the 0x prefix so it knows it is hex
unsigned int x = lexical_cast<unsigned int>("0xdeadbeef"); 
Evan Teran
fonte
1
Quando utilizar este método, que acabam com um valor de número inteiro de 152144602
Clayton
@ jmanning2k, sim, é estranho que tanto o boost quanto o meu lexical_cast vomitem strings hex (mesmo com o prefixo 0x) se eu não colocar std :: hex na string.
Evan Teran
1
@ SteveWilkinson: Leia o parágrafo começando com " EDIT ". Ele explica como você precisa usarstd::hex
Evan Teran
1
Para stringstreams, deve-se verificar ss.good() && ss.eof()se nenhum erro ocorreu.
AtoMerz
1
este "stoul" Salvar minha lógica
vincenzopalazzo
60

Para um método que funciona com C e C ++, você pode considerar o uso da função de biblioteca padrão 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
fonte
2
Você strtoulnão deve usar strtol. Haverá + overflowao usar strtol. Com, strtoulnão haverá excesso e o valor retornado será convertido longpara produzir o resultado correto (-65538). Então, sua resposta quase certo :)
Kirill V. Lyadvinsky
8
+1. Porque strtol (ou strtoul) mais rápido do que usar stringstream.
Kirill V. Lyadvinsky #
27

Andy Buchanan, no que diz respeito ao C ++, gostei do seu, mas tenho alguns 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;
    }
};

Usado como

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

Dessa forma, você não precisa de um impl impl por tipo.

Mike Lundy
fonte
1
Eu teria dado esse passo também, mas acho que gosto de limitar a proliferação de colchetes angulares. Nesse caso, senti que a regra "não duplicar código" era justificada. :-)
Andy J Buchanan
Infelizmente, é necessário, mas bem feito. Adicionado ao meu cabeçalho pessoal de extensões / correções de STL / Boost. Obrigado!
Tim Sylvester
Infelizmente, isso funciona apenas para conversões não assinadas. Portanto, você não pode converter 0xFFFFFFFF em -1.
fmuecke
@fmuecke: Isso ocorre porque 0xFFFFFFFF é um estouro de número inteiro assinado, que é um comportamento indefinido.
Billy ONeal
15

Exemplo de trabalho com strtoulserá:

#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 stringpara long. No meu computador numeric_limits<long>::max()0x7fffffff. Obviamente, isso 0xfffefffeé maior que 0x7fffffff. Então, strtolretorna em MAX_LONGvez do valor desejado. strtoulconverte stringparaunsigned long é por isso que nenhum estouro neste caso.

Ok, strtolestá considerando a sequência de entrada não como um inteiro assinado de 32 bits antes da conversão. Exemplo engraçado com 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;
    }
}

O código acima é impresso -65538no console.

Kirill V. Lyadvinsky
fonte
9

Aqui está um método simples e de trabalho que encontrei em outro lugar:

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

Observe que você pode preferir usar um número inteiro longo / inteiro longo não assinado para receber o valor. Outra observação, a função c_str () apenas converte o std :: string em const char *.

Portanto, se você tiver um const char * pronto, continue usando o nome da variável diretamente, como mostrado abaixo [Eu também estou mostrando o uso da variável longa não assinada para um número hexadecimal maior. Não confunda com o caso de ter const char * em vez de 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);

Isso funciona perfeitamente bem (desde que você use tipos de dados apropriados de acordo com sua necessidade).

AamodG
fonte
6

Eu tive o mesmo problema hoje, eis como o resolvi para que eu pudesse continuar 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...

(Encontrei esta página quando estava procurando uma maneira menos chata :-)

Cheers, A.

Andy J Buchanan
fonte
1
O código possui um erro trivial de compilação - outvalue não está definido (deve ser outValue).
Gabi Davar
3

Isso funcionou para mim:

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

Tente isso. Esta solução é um pouco arriscada. Não há cheques. A sequência deve ter apenas valores hexadecimais e o comprimento da sequência deve corresponder ao tamanho do tipo de retorno. Mas não há necessidade de cabeçalhos extras.

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: o comprimento da string deve ser divisível por 2

Johannes Beutel
fonte