web-dev-qa-db-fra.com

comment imprimer un caractère non signé en hexadécimal en c ++ en utilisant ostream?

Je veux travailler avec des variables 8 bits non signées en C++. Non plus unsigned char ou uint8_t faire l'affaire en ce qui concerne l'arithmétique (ce qui est attendu, puisque AFAIK uint8_t n'est qu'un alias pour unsigned char, ou du moins le débogueur le présente.

Le problème est que si j'imprime les variables à l'aide d'ostream en C++, il les traite comme des caractères. Si j'ai:

unsigned char a = 0;
unsigned char b = 0xff;
cout << "a is " << hex << a <<"; b is " << hex << b << endl;

alors la sortie est:

a is ^@; b is 377

au lieu de

a is 0; b is ff

J'ai essayé d'utiliser uint8_t, mais comme je l'ai déjà mentionné, c'est typé par unsigned char, donc il fait de même. Comment imprimer correctement mes variables?

Edit: Je le fais à plusieurs endroits dans mon code. Existe-t-il un moyen de faire cela sans transtyper en int chaque fois que je veux imprimer?

65
Nathan Fellman

Je suggère d'utiliser la technique suivante:

struct HexCharStruct
{
  unsigned char c;
  HexCharStruct(unsigned char _c) : c(_c) { }
};

inline std::ostream& operator<<(std::ostream& o, const HexCharStruct& hs)
{
  return (o << std::hex << (int)hs.c);
}

inline HexCharStruct hex(unsigned char _c)
{
  return HexCharStruct(_c);
}

int main()
{
  char a = 131;
  std::cout << hex(a) << std::endl;
}

Il est court à écrire, a la même efficacité que la solution d'origine et vous permet de choisir d'utiliser la sortie de caractères "d'origine". Et il est sûr pour le type (n'utilise pas de macros "diaboliques" :-))

50
MartinStettner

Utilisation:

cout << "a is " << hex << (int) a <<"; b is " << hex << (int) b << endl;

Et si vous voulez un remplissage avec des zéros en tête, alors:

#include <iomanip>
...
cout << "a is " << setw(2) << setfill('0') << hex << (int) a ; 

Comme nous utilisons des transtypages de style C, pourquoi ne pas aller tout le porc avec la bêtise du terminal C++ et utiliser une macro!

#define HEX( x )
   setw(2) << setfill('0') << hex << (int)( x )

vous pouvez alors dire

cout << "a is " << HEX( a );

Edit: Cela dit, la solution de MartinStettner est beaucoup plus agréable!

45
anon

Vous pouvez en savoir plus à ce sujet sur http://cpp.indi.frih.net/blog/2014/09/tippet-printing-numeric-values-for-chars-and-uint8_t/ and - http://cpp.indi.frih.net/blog/2014/08/code-critique-stack-overflow-posters-cant-print-the-numeric-value-of-a-char/ . Je ne poste cela que parce qu'il est devenu clair que l'auteur des articles ci-dessus n'a pas l'intention de le faire.

La technique la plus simple et la plus correcte pour imprimer un caractère en hexadécimal est

unsigned char a = 0;
unsigned char b = 0xff;
auto flags = cout.flags(); //I only include resetting the ioflags because so
                           //many answers on this page call functions where
                           //flags are changed and leave no way to  
                           //return them to the state they were in before 
                           //the function call
cout << "a is " << hex << +a <<"; b is " << +b << endl;
cout.flags(flags);

Les lecteurs résument la façon dont cela fonctionne, c'est que l'opérateur unaire + force une conversion de type sans op en un int avec la signature correcte. Ainsi, un caractère non signé est converti en entier non signé, un caractère signé est converti en entier et un caractère est converti en entier ou entier non signé selon que le caractère est signé ou non sur votre plate-forme (il est surprenant pour beaucoup que le caractère soit spécial et non spécifié comme signé ou non signé).

Le seul point négatif de cette technique est qu'il peut ne pas être évident de savoir ce qui arrive à une personne qui ne la connaît pas. Cependant, je pense qu'il vaut mieux utiliser la technique qui est correcte et en enseigner aux autres plutôt que de faire quelque chose qui est incorrect mais plus immédiatement clair.

30
ltc

Eh bien, cela fonctionne pour moi:

std::cout << std::hex << (0xFF & a) << std::endl;

Si vous venez de lancer (int) comme suggéré, il pourrait ajouter 1s à gauche de a si son bit le plus significatif est 1. Donc, faire cette opération binaire ET garantit que la sortie aura les bits de gauche remplis de 0 et les convertira également en entier non signé forçant cout à l'imprimer en hexadécimal.

J'espère que ça aide.

7
VinGarcia

Hm, il semble que j'ai réinventé la roue hier ... Mais bon, au moins c'est une roue générique cette fois :) chars sont imprimés avec deux chiffres hexadécimaux, shorts avec 4 chiffres hexadécimaux etc.

template<typename T>
struct hex_t
{
    T x;
};

template<typename T>
hex_t<T> hex(T x)
{
    hex_t<T> h = {x};
    return h;
}

template<typename T>
std::ostream& operator<<(std::ostream& os, hex_t<T> h)
{
    char buffer[2 * sizeof(T)];
    for (auto i = sizeof buffer; i--; )
    {
        buffer[i] = "0123456789ABCDEF"[h.x & 15];
        h.x >>= 4;
    }
    os.write(buffer, sizeof buffer);
    return os;
}
5
fredoverflow

Je pense que la réponse de TrungTN et anon est correcte, mais la manière de MartinStettner d'implémenter la fonction hex () n'est pas vraiment simple et trop sombre, étant donné que hex << (int) mychar est déjà une solution de contournement.

voici ma solution pour rendre l'opérateur "<<" plus facile:

#include <sstream>
#include <iomanip>

string uchar2hex(unsigned char inchar)
{
  ostringstream oss (ostringstream::out);
  oss << setw(2) << setfill('0') << hex << (int)(inchar);
  return oss.str();
}

int main()
{
  unsigned char a = 131;
  std::cout << uchar2hex(a) << std::endl;
}

Il n'est tout simplement pas digne d'implémenter un opérateur de flux :-)

4
tdihp

Je le ferais comme MartinStettner mais j'ajouterais un paramètre supplémentaire pour le nombre de chiffres:

inline HexStruct hex(long n, int w=2)
{
  return HexStruct(n, w);
}
// Rest of implementation is left as an exercise for the reader

Vous disposez donc de deux chiffres par défaut, mais vous pouvez en définir quatre, huit ou peu importe si vous le souhaitez.

par exemple.

int main()
{
  short a = 3142;
  std:cout << hex(a,4) << std::endl;
}

Cela peut sembler exagéré mais comme l'a dit Bjarne: "les bibliothèques devraient être faciles à utiliser, pas faciles à écrire".

4
Jimmy J

Je voudrais suggerer:

std::cout << setbase(16) << 32;

Tiré de: http://www.cprogramming.com/tutorial/iomanip.html

2
nikolajsheller

J'utilise ce qui suit sur win32/linux (32/64 bits):

#include <iostream>
#include <iomanip>

template <typename T>
std::string HexToString(T uval)
{
    std::stringstream ss;
    ss << "0x" << std::setw(sizeof(uval) * 2) << std::setfill('0') << std::hex << +uval;
    return ss.str();
}
2
drus

Vous pouvez essayer le code suivant:

unsigned char a = 0;
unsigned char b = 0xff;
cout << hex << "a is " << int(a) << "; b is " << int(b) << endl;
cout << hex
     <<   "a is " << setfill('0') << setw(2) << int(a)
     << "; b is " << setfill('0') << setw(2) << int(b)
     << endl;
cout << hex << uppercase
     <<   "a is " << setfill('0') << setw(2) << int(a)
     << "; b is " << setfill('0') << setw(2) << int(b)
     << endl;

Sortie:

a is 0; b is ff

a is 00; b is ff

a is 00; b is FF

2
TrungTN

Je me rends compte que c'est une vieille question, mais c'est aussi un résultat majeur de Google dans la recherche d'une solution à un problème très similaire que j'ai, qui est le désir d'implémenter des conversions arbitraires entières en chaînes hexadécimales dans une classe de modèle. Mon objectif final était en fait un Gtk::Entry modèle de sous-classe qui permettrait de modifier différentes largeurs entières en hexadécimal, mais c'est à côté du point.

Cela combine l'unaire operator+ astuce avec std::make_unsigned de <type_traits> pour éviter le problème de l'extension du signe négatif int8_t ou signed char valeurs qui se produisent dans cette réponse

Quoi qu'il en soit, je pense que c'est plus succinct que toute autre solution générique. Cela devrait fonctionner pour any types entiers signés ou non signés, et génère une erreur de compilation si vous essayez d'instancier la fonction avec des types non entiers.

template < 
  typename T,
  typename = typename std::enable_if<std::is_integral<T>::value, T>::type
>
std::string toHexString(const T v)
{ 
  std::ostringstream oss;
  oss << std::hex << +((typename std::make_unsigned<T>::type)v);
  return oss.str();
}

Quelques exemples d'utilisation:

int main(int argc, char**argv)
{
  int16_t val;
  // Prints 'ff' instead of "ffffffff". Unlike the other answer using the '+'
  // operator to extend sizeof(char) int types to int/unsigned int
  std::cout << toHexString(int8_t(-1)) << std::endl;

  // Works with any integer type
  std::cout << toHexString(int16_t(0xCAFE)) << std::endl;

  // You can use setw and setfill with strings too -OR- 
  // the toHexString could easily have parameters added to do that.
  std::cout << std::setw(8) << std::setfill('0') << 
    toHexString(int(100)) << std::endl;
  return 0;
}

Mise à jour: Alternativement, si vous n'aimez pas l'idée d'utiliser ostringstream, vous pouvez combiner l'astuce de modèle et d'opérateur unaire avec la solution basée sur la structure de la réponse acceptée pour les éléments suivants. Notez qu'ici, j'ai modifié le modèle en supprimant la vérification des types entiers. Le make_unsigned l'utilisation peut être suffisante pour compiler les garanties de sécurité de type heure.

template <typename T>
struct HexValue 
{
  T value;
  HexValue(T _v) : value(_v) { }
};

template <typename T>
inline std::ostream& operator<<(std::ostream& o, const HexValue<T>& hs)
{
  return o << std::hex << +((typename std::make_unsigned<T>::type) hs.value);
}

template <typename T>
const HexValue<T> toHex(const T val)
{
  return HexValue<T>(val);
}

// Usage:
std::cout << toHex(int8_t(-1)) << std::endl;
1
Brian McFarland

Je voudrais publier ma version réinventée basée sur @ FredOverflow. J'ai apporté les modifications suivantes.

réparer:

  • Les Rhs de operator<< Doivent être de type de référence const. Dans le code de @ FredOverflow, h.x >>= 4 Modifie la sortie h, ce qui n'est étonnamment pas compatible avec la bibliothèque standard, et le type T doit être construit en copie.
  • Supposons que CHAR_BITS Est un multiple de 4. @ Le code de FredOverflow suppose que char est de 8 bits, ce qui n'est pas toujours vrai, dans certaines implémentations sur les DSP, en particulier, il n'est pas rare que char est 16 bits, 24 bits, 32 bits, etc.

améliorer:

  • Prend en charge tous les autres manipulateurs de bibliothèque standard disponibles pour les types intégraux, par exemple std::uppercase. Étant donné que la sortie au format est utilisée dans _print_byte, Les manipulateurs de bibliothèque standard sont toujours disponibles.
  • Ajoutez hex_sep Pour imprimer des octets séparés (notez qu'en C/C++, un "octet" est par définition une unité de stockage de la taille de char). Ajoutez un paramètre de modèle Sep et instanciez _Hex<T, false> Et _Hex<T, true> Dans hex et hex_sep Respectivement.
  • Évitez le ballonnement de code binaire. La fonction _print_byte Est extraite de operator<<, Avec un paramètre de fonctionsize, pour éviter l'instanciation de différents Size.

Plus d'informations sur le ballonnement de code binaire:

Comme mentionné dans l'amélioration 3, peu importe à quel point hex et hex_sep Sont utilisés, seules deux copies de la fonction (presque) dupliquée sortiront en code binaire: _print_byte<true> Et _print_byte<false>. Et vous pourriez vous rendre compte que cette duplication peut également être éliminée en utilisant exactement la même approche: ajoutez un paramètre de fonction sep. Oui, mais si vous le faites, un runtime if(sep) est nécessaire. Je veux un utilitaire de bibliothèque commun qui peut être largement utilisé dans le programme, j'ai donc compromis la duplication plutôt que la surcharge d'exécution. J'y suis parvenu en utilisant le temps de compilation if: C++ 11 std::conditional, La surcharge de l'appel de fonction peut être optimisée avec inline.

hex_print.h:

namespace Hex
{
typedef unsigned char Byte;

template <typename T, bool Sep> struct _Hex
{
    _Hex(const T& t) : val(t)
    {}
    const T& val;
};

template <typename T, bool Sep>
std::ostream& operator<<(std::ostream& os, const _Hex<T, Sep>& h);
}

template <typename T>  Hex::_Hex<T, false> hex(const T& x)
{ return Hex::_Hex<T, false>(x); }

template <typename T>  Hex::_Hex<T, true> hex_sep(const T& x)
{ return Hex::_Hex<T, true>(x); }

#include "misc.tcc"

hex_print.tcc:

namespace Hex
{

struct Put_space {
    static inline void run(std::ostream& os) { os << ' '; }
};
struct No_op {
    static inline void run(std::ostream& os) {}
};

#if (CHAR_BIT & 3) // can use C++11 static_assert, but no real advantage here
#error "hex print utility need CHAR_BIT to be a multiple of 4"
#endif
static const size_t width = CHAR_BIT >> 2;

template <bool Sep>
std::ostream& _print_byte(std::ostream& os, const void* ptr, const size_t size)
{
    using namespace std;

    auto pbyte = reinterpret_cast<const Byte*>(ptr);

    os << hex << setfill('0');
    for (int i = size; --i >= 0; )
    {
        os << setw(width) << static_cast<short>(pbyte[i]);
        conditional<Sep, Put_space, No_op>::type::run(os);
    }
    return os << setfill(' ') << dec;
}

template <typename T, bool Sep>
inline std::ostream& operator<<(std::ostream& os, const _Hex<T, Sep>& h)
{
    return _print_byte<Sep>(os, &h.val, sizeof(T));
}

}

tester:

struct { int x; } output = {0xdeadbeef};
cout << hex_sep(output) << std::uppercase << hex(output) << endl;

sortie:

de ad be ef DEADBEEF

1
wpzdm