how do I print an unsigned char as hex in c++ using ostream?
Asked Answered
M

17

87

I want to work with unsigned 8-bit variables in C++. Either unsigned char or uint8_t do the trick as far as the arithmetic is concerned (which is expected, since AFAIK uint8_t is just an alias for unsigned char, or so the debugger presents it.

The problem is that if I print out the variables using ostream in C++ it treats it as char. If I have:

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

then the output is:

a is ^@; b is 377

instead of

a is 0; b is ff

I tried using uint8_t, but as I mentioned before, that's typedef'ed to unsigned char, so it does the same. How can I print my variables correctly?

Edit: I do this in many places throughout my code. Is there any way I can do this without casting to int each time I want to print?

Metaxylem answered 23/3, 2009 at 12:42 Comment(2)
I think MartinStettner's answer is rather confusing, I don't think it is worth to implement an extra struct and an extra stream operator. anon's solution is straight forward and works good enough for me.Saltus
mark: In C++, std::cout treats variables of type char and unsigned char as character types, and outputs them as characters. To output their integer values, it is necessary to cast them to int type. std::cout << std::setw(2) << std::setfill('0') << std::hex << (int)(unsigned char)c; // or std::cout << std::setw(2) << std::setfill('0') << std::hex << (c & 0xff);Edmee
Z
59

I would suggest using the following technique:

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

It's short to write, has the same efficiency as the original solution and it lets you choose to use the "original" character output. And it's type-safe (not using "evil" macros :-))

Zoophobia answered 23/3, 2009 at 13:37 Comment(5)
As long as we're eschewing macros: in order to be more fully C++, shouldn't you write (int)hs.c as static_cast<int>(hs.c)? :PMarlysmarmaduke
There is a slight bug in your code. If you feed a negative character in the code the hex value becomes 4 bytes instead of 2. Switching to unsigned char fixes the problem.Garden
Another bug(?) is that the above operator<< changes the mode of the given stream to hexadecimal: cout << hex(a) << 100 will give you a surprise. You should store the state of the stream before modifying it, and restore it later.Aretta
I'm truly amazed at how bad C++ is at converting char to hex.Rosenbaum
@CraigRinger, to make the fun even more exquisite: cout << hex << (int)'x' << ", " << (uint8_t)'x'; would still only give hex for the first one, and not the second, as uint8_t is not even a number, but just a char typedef. And sure enough ... << (byte)'x' wouldn't even compile, as there's no << for byte. :) I't hilarious indeed.Buprestid
N
62

Use:

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

And if you want padding with leading zeros then:

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

As we are using C-style casts, why not go the whole hog with terminal C++ badness and use a macro!

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

you can then say

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

Edit: Having said that, MartinStettner's solution is much nicer!

Nellynelms answered 23/3, 2009 at 12:44 Comment(3)
Can I convince you to eschew the evil C-style casts once and for all? #528499Maje
Not in this case - it's about the only place I think they are justified.Nellynelms
I would do it the same, except to avoid the cast I would use cout << hex << int(a); It means the same thing as a cast, without the cast. :)Miso
Z
59

I would suggest using the following technique:

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

It's short to write, has the same efficiency as the original solution and it lets you choose to use the "original" character output. And it's type-safe (not using "evil" macros :-))

Zoophobia answered 23/3, 2009 at 13:37 Comment(5)
As long as we're eschewing macros: in order to be more fully C++, shouldn't you write (int)hs.c as static_cast<int>(hs.c)? :PMarlysmarmaduke
There is a slight bug in your code. If you feed a negative character in the code the hex value becomes 4 bytes instead of 2. Switching to unsigned char fixes the problem.Garden
Another bug(?) is that the above operator<< changes the mode of the given stream to hexadecimal: cout << hex(a) << 100 will give you a surprise. You should store the state of the stream before modifying it, and restore it later.Aretta
I'm truly amazed at how bad C++ is at converting char to hex.Rosenbaum
@CraigRinger, to make the fun even more exquisite: cout << hex << (int)'x' << ", " << (uint8_t)'x'; would still only give hex for the first one, and not the second, as uint8_t is not even a number, but just a char typedef. And sure enough ... << (byte)'x' wouldn't even compile, as there's no << for byte. :) I't hilarious indeed.Buprestid
S
41

You can read more about this at 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/. I am only posting this because it has become clear that the author of the above articles does not intend to.

The simplest and most correct technique to do print a char as hex is

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

The readers digest version of how this works is that the unary + operator forces a no op type conversion to an int with the correct signedness. So, an unsigned char converts to unsigned int, a signed char converts to int, and a char converts to either unsigned int or int depending on whether char is signed or unsigned on your platform (it comes as a shock to many that char is special and not specified as either signed or unsigned).

The only negative of this technique is that it may not be obvious what is happening to a someone that is unfamiliar with it. However, I think that it is better to use the technique that is correct and teach others about it rather than doing something that is incorrect but more immediately clear.

Santos answered 5/2, 2015 at 22:23 Comment(4)
Why is this not the top answer? It is short and effective.Biblical
This technique sign-extends types smaller than unsigned int / int, which means for negative signed chars (or int16_t for example), you'll get 8 nibbles of output, the first 6 of which are F.Salim
@BrianMcFarland I noticed that issue even for unsigned char (when greater than 127), so I solved it with a bitmask: (+c & 0xFF).Athanasia
@To마SE, the unary + is unnecessary, the binary & already converts to an int.Allhallowtide
D
36

Well, this works for me:

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

If you just cast (int) as suggested it might add 1s to the left of a if its most significant bit is 1. So making this binary AND operation guarantees the output will have the left bits filled by 0s and also converts it to unsigned int forcing cout to print it as hex.

I hope this helps.

Dannettedanni answered 1/3, 2018 at 16:16 Comment(4)
This is the simplest and cleanest method of the bunch.Allhallowtide
I really like this answer too! I used this solution.Obligate
This works, but any suggestion on how I can prefix characters <= 9 to have a 0 prepended?Immobilize
@Shn_Android_Dev you can do it like this: #1715015Dannettedanni
R
26

In C++20 you can use std::format to do this:

std::cout << std::format("a is {:x}; b is {:x}\n", a, b);

Output:

a is 0; b is ff

If it is not available you can use the {fmt} library, std::format is based on. {fmt} also provides the print function that makes this even easier and more efficient (godbolt):

fmt::print("a is {:x}; b is {:x}\n", a, b);

Disclaimer: I'm the author of {fmt} and C++20 std::format.

Reckless answered 15/12, 2020 at 15:32 Comment(1)
Best modern response. No need to muck about with std::hex, std::uppercase, and all that jazz when I can just do std::cout << std::format("0x{:02X} ", (unsigned char)ch);Dhahran
A
6

Hm, it seems I re-invented the wheel yesterday... But hey, at least it's a generic wheel this time :) chars are printed with two hex digits, shorts with 4 hex digits and so on.

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;
}
Arista answered 4/9, 2013 at 10:30 Comment(1)
I like it. Succinct. No redundant info required. I'd love a bool to indicate whether upper or lowercase is desired. Or honour the std::uppercase and std::nouppercase manipulators (which manipulate the std::ios_base::flags::uppercase iosflag)Tartaric
S
5

I think TrungTN and anon's answer is okay, but MartinStettner's way of implementing the hex() function is not really simple, and too dark, considering hex << (int)mychar is already a workaround.

here is my solution to make "<<" operator easier:

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

It's just not worthy implementing a stream operator :-)

Saltus answered 28/9, 2012 at 8:51 Comment(0)
A
5

I think we are missing an explanation of how these type conversions work.

char is platform dependent signed or unsigned. In x86 char is equivalent to signed char.

When an integral type (char, short, int, long) is converted to a larger capacity type, the conversion is made by adding zeros to the left in case of unsigned types and by sign extension for signed ones. Sign extension consists in replicating the most significant (leftmost) bit of the original number to the left till we reach the bit size of the target type.

Hence if I am in a signed char by default system and I do this:

char a = 0xF0; // Equivalent to the binary: 11110000
std::cout << std::hex << static_cast<int>(a);

We would obtain F...F0 since the leading 1 bit has been extended.

If we want to make sure that we only print F0 in any system we would have to make an additional intermediate type cast to an unsigned char so that zeros are added instead and, since they are not significant for a integer with only 8-bits, not printed:

char a = 0xF0; // Equivalent to the binary: 11110000
std::cout << std::hex << static_cast<int>(static_cast<unsigned char>(a));

This produces F0

Antemortem answered 13/3, 2020 at 14:55 Comment(0)
S
3

I'd do it like MartinStettner but add an extra parameter for number of digits:

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

So you have two digits by default but can set four, eight, or whatever if you want to.

eg.

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

It may seem like overkill but as Bjarne said: "libraries should be easy to use, not easy to write".

Stradivari answered 23/3, 2009 at 14:0 Comment(0)
A
2

I would suggest:

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

Taken from: http://www.cprogramming.com/tutorial/iomanip.html

Asteria answered 2/2, 2012 at 15:23 Comment(0)
G
2

You can try the following code:

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;

Output:

a is 0; b is ff

a is 00; b is ff

a is 00; b is FF

Glanti answered 7/5, 2012 at 2:27 Comment(1)
While this does solve the problem, one of my requirements was not to have to cast to int whenever I want to do this, since this appears too many times in the code.Metaxylem
Y
2

I use the following on win32/linux(32/64 bit):

#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();
}
Yonkers answered 16/5, 2016 at 9:18 Comment(0)
S
1

I realize this is an old question, but its also a top Google result in searching for a solution to a very similar problem I have, which is the desire to implement arbitrary integer to hex string conversions within a template class. My end goal was actually a Gtk::Entry subclass template that would allow editing various integer widths in hex, but that's beside the point.

This combines the unary operator+ trick with std::make_unsigned from <type_traits> to prevent the problem of sign-extending negative int8_t or signed char values that occurs in this answer

Anyway, I believe this is more succinct than any other generic solution. It should work for any signed or unsigned integer types, and throws a compile-time error if you attempt to instantiate the function with any non-integer types.

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

Some example usage:

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

Update: Alternatively, if you don't like the idea of the ostringstream being used, you can combine the templating and unary operator trick with the accepted answer's struct-based solution for the following. Note that here, I modified the template by removing the check for integer types. The make_unsigned usage might be enough for compile time type safety guarantees.

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;
Salim answered 13/3, 2016 at 4:54 Comment(0)
R
0

I'd like to post my re-re-inventing version based on @FredOverflow's. I made the following modifications.

fix:

  • Rhs of operator<< should be of const reference type. In @FredOverflow's code, h.x >>= 4 changes output h, which is surprisingly not compatible with standard library, and type T is requared to be copy-constructable.
  • Assume only CHAR_BITS is a multiple of 4. @FredOverflow's code assumes char is 8-bits, which is not always true, in some implementations on DSPs, particularly, it is not uncommon that char is 16-bits, 24-bits, 32-bits, etc.

improve:

  • Support all other standard library manipulators available for integral types, e.g. std::uppercase. Because format output is used in _print_byte, standard library manipulators are still available.
  • Add hex_sep to print separate bytes (note that in C/C++ a 'byte' is by definition a storage unit with the size of char). Add a template parameter Sep and instantiate _Hex<T, false> and _Hex<T, true> in hex and hex_sep respectively.
  • Avoid binary code bloat. Function _print_byte is extracted out of operator<<, with a function parameter size, to avoid instantiation for different Size.

More on binary code bloat:

As mentioned in improvement 3, no matter how extensively hex and hex_sep is used, only two copies of (nearly) duplicated function will exits in binary code: _print_byte<true> and _print_byte<false>. And you might realized that this duplication can also be eliminated using exactly the same approach: add a function parameter sep. Yes, but if doing so, a runtime if(sep) is needed. I want a common library utility which may be used extensively in the program, thus I compromised on the duplication rather than runtime overhead. I achieved this by using compile-time if: C++11 std::conditional, the overhead of function call can hopefully be optimized away by 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));
}

}

test:

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

output:

de ad be ef DEADBEEF

Raseda answered 1/11, 2013 at 12:50 Comment(0)
M
0

If you're using prefill and signed chars, be careful not to append unwanted 'F's

char out_character = 0xBE; cout << setfill('0') << setw(2) << hex << unsigned short(out_character);

prints: ffbe

using int instead of short results in ffffffbe

To prevent the unwanted f's you can easily mask them out.

char out_character = 0xBE; cout << setfill('0') << setw(2) << hex << unsigned short(out_character) & 0xFF;

Mcglynn answered 13/11, 2019 at 16:56 Comment(0)
S
-2

This will also work:

std::ostream& operator<< (std::ostream& o, unsigned char c)
{
    return o<<(int)c;
}

int main()
{
    unsigned char a = 06;
    unsigned char b = 0xff;
    std::cout << "a is " << std::hex << a <<"; b is " << std::hex << b << std::endl;
    return 0;
}
Shakta answered 23/3, 2009 at 13:1 Comment(1)
But then he has to use casts if he actually wants them output as chars, which seems a little unnatural!Nellynelms
A
-2

I have used in this way.

    char strInput[] = "yourchardata";
char chHex[2] = "";

int nLength = strlen(strInput);
char* chResut = new char[(nLength*2) + 1];
memset(chResut, 0, (nLength*2) + 1);



for (int i = 0; i < nLength; i++)
{
    sprintf(chHex, "%02X", strInput[i]& 0x00FF);    
    memcpy(&(chResut[i*2]), chHex, 2);
}

printf("\n%s",chResut);
delete chResut;
chResut = NULL;
Asphyxiant answered 4/9, 2013 at 9:58 Comment(1)
This is a question about C++ and std::ostream not about C and printf. :)Dictation

© 2022 - 2024 — McMap. All rights reserved.