C ++ convierte cadena hexadecimal a entero con signo

135

Quiero convertir una cadena hexadecimal a un entero con signo de 32 bits en C ++.

Entonces, por ejemplo, tengo la cadena hexadecimal "fffefffe". La representación binaria de esto es 11111111111111101111111111111110. La representación entera firmada de esto es: -65538.

¿Cómo hago esta conversión en C ++? Esto también debe funcionar para números no negativos. Por ejemplo, la cadena hexadecimal "0000000A", que es 00000000000000000000000000001010 en binario y 10 en decimal.

Clayton
fuente
2
Nota. Solo obtendrá -65538 para sistemas donde sizeof (int) == 4
Martin York
3
@ Martin York, no lo mencionó int. "32 bit entero con signo" podría ser int32_t o __int32 etc.
Kirill V. Lyadvinsky

Respuestas:

230

utilizar std::stringstream

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

el siguiente ejemplo produce -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;
}

¡En el nuevo estándar C ++ 11, hay algunas nuevas funciones de utilidad que puede utilizar! específicamente, hay una familia de funciones "string to number" ( http://en.cppreference.com/w/cpp/string/basic_string/stol y http://en.cppreference.com/w/cpp/string/ basic_string / stoul ). Estos son esencialmente envoltorios delgados alrededor de las funciones de conversión de cadena a número de C, pero saben cómo lidiar con unstd::string

Entonces, la respuesta más simple para el código más nuevo probablemente se vería así:

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

NOTA: A continuación se muestra mi respuesta original, que como dice la edición no es una respuesta completa. Para una solución funcional, pegue el código sobre la línea :-).

Parece que ya lexical_cast<>está definido para tener semántica de conversión de flujo. Lamentablemente, las transmisiones no entienden la notación "0x". Así que tanto la boost::lexical_castmano como la mía no se manejan bien con cuerdas hexagonales. La solución anterior que establece manualmente el flujo de entrada en hexadecimal lo manejará bien.

Boost también tiene algunas cosas para hacer esto, que también tiene algunas buenas capacidades de verificación de errores. Puedes usarlo así:

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

Si no tiene ganas de usar boost, aquí hay una versión ligera del elenco léxico que no realiza ninguna comprobación de errores:

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

que puedes usar así:

// though this needs the 0x prefix so it knows it is hex
unsigned int x = lexical_cast<unsigned int>("0xdeadbeef"); 
Evan Teran
fuente
1
Cuando uso ese método, termino con un valor entero de 152144602
Clayton
@ jmanning2k, sí, es extraño que tanto boost como mi lexical_cast barf en cadenas hexadecimales (incluso con el prefijo 0x) si no pongo std :: hex en la cadena.
Evan Teran
1
@SteveWilkinson: Lea el párrafo que comienza con " EDITAR ". Explica cómo debe usarlostd::hex
Evan Teran
1
Por stringstreams uno debe verificar ss.good() && ss.eof()para asegurarse de que no se produjeron errores.
atoMerz
1
este "alma" salva mi lógica
vincenzopalazzo
60

Para un método que funciona con C y C ++, es posible que desee considerar el uso de la función de biblioteca estándar 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
fuente
2
Debe utilizar strtoulno strtol. Habrá + overflowal usar strtol. Con strtoulno habrá desbordamiento y el valor devuelto se convertirá longpara producir el resultado correcto (-65538). Así que su respuesta es casi correcta :)
Kirill V. Lyadvinsky
8
+1. Porque strtol (o strtoul) más rápido que usar stringstream.
Kirill V. Lyadvinsky 01 de
27

Andy Buchanan, en lo que respecta a C ++, me gustó el tuyo, pero tengo algunas modificaciones:

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

Utilizado como

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

De esa manera no necesita una impl por tipo int.

Mike Lundy
fuente
1
También habría dado ese paso, pero creo que me gusta limitar la proliferación de paréntesis angulares. Para este caso, sentí que estaba justificado romper la regla de "no duplicar código". :-)
Andy J Buchanan
Lamentablemente es necesario, pero está bien hecho. Agregado a mi encabezado STL / Boost extensiones / arreglos personales. ¡Gracias!
Tim Sylvester
Lamentablemente, esto solo funciona para la conversión sin firmar. Por lo tanto, no puede convertir 0xFFFFFFFF a -1.
fmuecke
@fmuecke: Esto se debe a que 0xFFFFFFFF es un desbordamiento de entero con signo, que es un comportamiento indefinido.
Billy ONeal
15

Ejemplo de trabajo con 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;
    }
}

strtolconvierte stringa long. En mi computadora numeric_limits<long>::max()da 0x7fffffff. Obviamente eso 0xfffefffees mayor que 0x7fffffff. Entonces strtoldevuelve en MAX_LONGlugar del valor deseado. strtoulse convierte stringpor unsigned longeso no hay desbordamiento en este caso.

Ok, strtolestá considerando la cadena de entrada no como un entero con signo de 32 bits antes de la conversión. Muestra divertida 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;
    }
}

El código anterior se imprime -65538en la consola.

Kirill V. Lyadvinsky
fuente
9

Aquí hay un método simple y de trabajo que encontré en otro lugar:

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

Tenga en cuenta que es posible que prefiera usar un entero largo sin signo / entero largo para recibir el valor. Otra nota, la función c_str () simplemente convierte el std :: string a const char *.

Entonces, si tiene listo un const char *, simplemente siga usando el nombre de esa variable directamente, como se muestra a continuación [También estoy mostrando el uso de la variable larga sin signo para un número hexadecimal más grande. No lo confunda con el caso de tener const char * en lugar 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);

Esto funciona perfectamente bien (siempre que use los tipos de datos apropiados según sus necesidades).

AamodG
fuente
6

Tuve el mismo problema hoy, así es como lo resolví para poder mantener 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...

(Encontré esta página cuando estaba buscando una manera menos desagradable :-)

Saludos, A.

Andy J Buchanan
fuente
1
El código tiene un error de compilación trivial: el valor no está definido (debe ser outValue).
Gabi Davar
3

Esto funcionó para mí:

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 
Miguel
fuente
0

Prueba esto. Esta solución es un poco arriesgada. No hay cheques. La cadena solo debe tener valores hexadecimales y la longitud de la cadena debe coincidir con el tamaño del tipo de retorno. Pero no hay necesidad de encabezados adicionales.

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 longitud de la cadena debe ser divisible por 2

Johannes Beutel
fuente