C ++ convertir une chaîne hexadécimale en entier signé

135

Je veux convertir une chaîne hexadécimale en un entier signé 32 bits en C ++.

Ainsi, par exemple, j'ai la chaîne hexadécimale "fffefffe". La représentation binaire de ceci est 111111111111111011111111111110. La représentation entière signée de ceci est: -65538.

Comment faire cette conversion en C ++? Cela doit également fonctionner pour les nombres non négatifs. Par exemple, la chaîne hexadécimale "0000000A", qui est 00000000000000000000000000001010 en binaire et 10 en décimal.

Clayton
la source
2
Remarque. Vous n'obtiendrez que -65538 pour les systèmes où sizeof (int) == 4
Martin York
3
@Martin York, il n'a pas mentionné int. "Entier signé 32 bits" pourrait être int32_t ou __int32 etc.
Kirill V. Lyadvinsky

Réponses:

230

utilisation std::stringstream

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

l'exemple suivant produit -65538comme résultat:

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

Dans le nouveau standard C ++ 11, vous pouvez utiliser quelques nouvelles fonctions utilitaires! plus précisément, il existe une famille de fonctions "chaîne à nombre" ( http://en.cppreference.com/w/cpp/string/basic_string/stol et http://en.cppreference.com/w/cpp/string/ basic_string / stoul ). Ce sont essentiellement des enveloppes minces autour des fonctions de conversion de chaîne en nombre de C, mais savent comment gérer unstd::string

Ainsi, la réponse la plus simple pour un code plus récent ressemblerait probablement à ceci:

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

REMARQUE: Voici ma réponse originale, qui, comme le dit la modification, n'est pas une réponse complète. Pour une solution fonctionnelle, collez le code au-dessus de la ligne :-).

Il semble que puisque lexical_cast<>est défini pour avoir une sémantique de conversion de flux. Malheureusement, les flux ne comprennent pas la notation "0x". Donc, le boost::lexical_castet ma main ont roulé un ne gèrent pas bien les cordes hexagonales. La solution ci-dessus qui définit manuellement le flux d'entrée sur hexa le gérera très bien.

Boost a également quelques trucs pour le faire, qui a également de bonnes capacités de vérification des erreurs. Vous pouvez l'utiliser comme ceci:

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

Si vous n'avez pas envie d'utiliser boost, voici une version allégée du cast lexical qui ne vérifie pas les erreurs:

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

que vous pouvez utiliser comme ceci:

// though this needs the 0x prefix so it knows it is hex
unsigned int x = lexical_cast<unsigned int>("0xdeadbeef"); 
Evan Teran
la source
1
Lorsque j'utilise cette méthode, je me retrouve avec une valeur entière de 152144602
Clayton
@ jmanning2k, oui, c'est bizarre que boost et mon barf lexical_cast sur des chaînes hexadécimales (même avec le préfixe 0x) si je ne mets pas std :: hex dans la chaîne.
Evan Teran
1
@SteveWilkinson: Lisez le paragraphe commençant par " EDIT ". Il explique comment vous devez utiliserstd::hex
Evan Teran
1
Pour stringstreams, il faut vérifier ss.good() && ss.eof()qu'aucune erreur ne s'est produite.
atoMerz
1
ce "stoul" sauve ma logique
vincenzopalazzo
60

Pour une méthode qui fonctionne à la fois avec C et C ++, vous pouvez envisager d'utiliser la fonction de bibliothèque 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;
    }
}
Geof Sawaya
la source
2
Vous ne devriez strtoulpas utiliser strtol. Il y en aura + overflowlors de l'utilisation de strtol. Avec strtoulil n'y aura pas de débordement et la valeur retournée sera convertie en longpour produire un résultat correct (-65538). Donc, votre réponse est presque correcte :)
Kirill V. Lyadvinsky
8
+1. Parce que strtol (ou strtoul) est plus rapide que l'utilisation de stringstream.
Kirill V. Lyadvinsky
27

Andy Buchanan, en ce qui concerne le respect du C ++, j'ai aimé le vôtre, mais j'ai quelques 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;
    }
};

Utilisé comme

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

De cette façon, vous n'avez pas besoin d'un impl par type int.

Mike Lundy
la source
1
J'aurais franchi cette étape aussi, mais je trouve que j'aime limiter la prolifération des chevrons. Dans ce cas, j'ai senti que la violation de la règle «ne pas dupliquer le code» était justifiée. :-)
Andy J Buchanan
Dommage que ce soit nécessaire, mais bien fait. Ajouté à mon en-tête personnel STL / Boost extensions / fixes. Merci!
Tim Sylvester
Malheureusement, cela ne fonctionne que pour la conversion non signée. Vous ne pouvez donc pas convertir 0xFFFFFFFF en -1.
fmuecke
@fmuecke: C'est parce que 0xFFFFFFFF est un débordement d'entier signé, ce qui est un comportement non défini.
Billy ONeal
15

Un exemple de travail avec strtoulsera:

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

strtolse convertit stringen long. Sur mon ordinateur numeric_limits<long>::max()donne 0x7fffffff. De toute évidence, c'est 0xfffefffesupérieur à 0x7fffffff. Donc strtolretourne MAX_LONGau lieu de la valeur souhaitée. strtoulconvertit stringen unsigned longc'est pourquoi aucun débordement dans ce cas.

Ok, strtolconsidère que la chaîne d'entrée n'est pas un entier signé de 32 bits avant la conversion. Échantillon amusant avec 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;
    }
}

Le code ci-dessus s'imprime -65538dans la console.

Kirill V. Lyadvinsky
la source
9

Voici une méthode simple et fonctionnelle que j'ai trouvée ailleurs:

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

Veuillez noter que vous préférerez peut-être utiliser un entier long non signé / un entier long pour recevoir la valeur. Une autre note, la fonction c_str () convertit simplement le std :: string en const char *.

Donc, si vous avez un const char * prêt, continuez simplement à utiliser ce nom de variable directement, comme indiqué ci-dessous [Je montre également l'utilisation de la variable longue non signée pour un nombre hexadécimal plus grand. Ne le confondez pas avec le cas d'avoir const char * au lieu 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);

Cela fonctionne parfaitement bien (à condition d'utiliser les types de données appropriés selon vos besoins).

AamodG
la source
6

J'ai eu le même problème aujourd'hui, voici comment je l'ai résolu pour que je puisse garder 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...

(J'ai trouvé cette page quand je cherchais un moyen moins nul :-)

Bravo, A.

Andy J Buchanan
la source
1
Le code a une erreur de compilation triviale - outvalue n'est pas défini (devrait être outValue).
Gabi Davar
3

Cela a fonctionné pour moi:

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
la source
0

Essaye ça. Cette solution est un peu risquée. Il n'y a pas de chèques. La chaîne ne doit avoir que des valeurs hexadécimales et la longueur de la chaîne doit correspondre à la taille du type de retour. Mais pas besoin d'en-têtes supplémentaires.

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

Usage:

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

Remarque: la longueur de la chaîne doit être divisible par 2

Johannes Beutel
la source