как напечатать беззнаковый символ как шестнадцатеричный в c++ с помощью ostream?
Я хочу работать с 8-битными переменными без знака в C++. Либо unsigned char
или uint8_t
сделайте трюк, насколько это касается арифметики (что ожидается, так как AFAIK uint8_t
- это просто псевдоним для unsigned char
, или так отладчик представляет его.
проблема в том, что если я распечатываю переменные с помощью ostream в C++, он обрабатывает их как char. Если у меня есть:
unsigned char a = 0;
unsigned char b = 0xff;
cout << "a is " << hex << a <<"; b is " << hex << b << endl;
тогда выход:
a is ^@; b is 377
вместо
a is 0; b is ff
I пробовал использовать uint8_t
, но, как я уже упоминал ранее, это typedef'ed к unsigned char
, поэтому он делает то же самое. Как я могу напечатать мои переменные правильно?
Edit: я делаю это во многих местах на протяжении всей моей код. Есть ли способ, которым я могу это сделать без кастинг int
каждый раз, когда я хочу напечатать?
14 ответов:
Я бы предложил использовать следующий прием:
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; }
он короткий для записи, имеет ту же эффективность, что и исходное решение, и позволяет вам использовать "оригинальный" вывод символов. И это типобезопасно (не используя" злые " макросы : -))
использование:
cout << "a is " << hex << (int) a <<"; b is " << hex << (int) b << endl;
и если вы хотите заполнение с ведущими нулями, то:
#include <iomanip> ... cout << "a is " << setw(2) << setfill('0') << hex << (int) a ;
как мы используем c-стиль бросает, почему бы не пойти всю свинью с терминалом c++ плохость и использовать макрос!
#define HEX( x ) setw(2) << setfill('0') << hex << (int)( x )
вы можете тогда сказать
cout << "a is " << HEX( a );
Edit: сказав это, решение Мартинстеттнера намного лучше!
вы можете прочитать больше об этом на http://cpp.indi.frih.net/blog/2014/09/tippet-printing-numeric-values-for-chars-and-uint8_t/ и http://cpp.indi.frih.net/blog/2014/08/code-critique-stack-overflow-posters-cant-print-the-numeric-value-of-a-char/я публикую это только потому, что стало ясно, что автор вышеуказанных статей не намерен.
самый простой и самый правильный метод, чтобы сделать печать char как наговор это
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);
версия Readers digest о том, как это работает, заключается в том, что унарный оператор + вызывает преобразование типа no op в int с правильной подписью. Так, без знака типа char преобразуются в unsigned int и подписанный Чаре преобразует в int, и символ превращается в любой неподписанных int или int в зависимости от того, тип char является знаковым или беззнаковым на вашей платформе (это приходит как шок для многих, что char является специальным и не указано как signed или unsigned).
только негатив этой техники заключается в том, что может быть не очевидно, что происходит с кем-то, кто не знаком с ней. Однако я думаю, что лучше использовать правильную технику и учить об этом других, а не делать что-то неправильное, но более ясное.
Я бы сделал это как MartinStettner но добавить дополнительный параметр для количества цифр:
inline HexStruct hex(long n, int w=2) { return HexStruct(n, w); } // Rest of implementation is left as an exercise for the reader
таким образом, у вас есть две цифры по умолчанию, но вы можете установить четыре, восемь или что угодно, если хотите.
например.
int main() { short a = 3142; std:cout << hex(a,4) << std::endl; }
это может показаться излишним, но, как сказал Бьярне: "библиотеки должны быть просты в использовании, а не легко писать".
Я думаю, что ответ TrungTN и anon в порядке, но способ реализации функции hex() Мартинстеттнера не очень прост и слишком темный, учитывая, что hex
вот мое решение, чтобы сделать "
#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; }
это просто не достойно реализации оператора потока :-)
хм, кажется, я заново изобрел колесо вчера... Но эй, по крайней мере, это общее колесо на этот раз :)
char
s печатаются с двумя шестнадцатеричными цифрами,short
s с 4 шестнадцатеричными цифрами и так далее.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; }
вы можете попробовать следующий код:
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;
выход:
a is 0; b is ff
a is 00; b is ff
a is 00; b is FF
Я использую следующее на win32 / linux (32/64 бит):
#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(); }
я хочу, чтобы мой пост заново изобретать версии, основанные на @FredOverflow по. Я сделал следующие изменения.
исправления:
- Рит
operator<<
должно бытьconst
ссылочный тип. В коде @FredOverflow,h.x >>= 4
изменения выходного сигналаh
, который на удивление не совместим со стандартной библиотекой, и введитеT
requared, чтобы быть copy-constructable.- предположим только
CHAR_BITS
кратно 4. Код @FredOverflow предполагаетchar
является 8-битным, что не всегда верно, в некоторых реализациях на DSPs, в частности, это не редкость, чтоchar
16 битов, 24 бита, 32 бита, etc.улучшения:
- поддержка всех других стандартных библиотечных манипуляторов, доступных для интегральных типов, например
std::uppercase
. Потому что формат вывода используется в_print_byte
, стандартные манипуляторы библиотеки все еще доступны.- добавить
hex_sep
для печати отдельных байтов (обратите внимание, что в C/C++ 'байт' является определение единицы хранения с размеромchar
). Добавьте параметр шаблонаSep
и инстанцировать_Hex<T, false>
и_Hex<T, true>
наhex
иhex_sep
соответственно.- избегайте двоичного кода раздувания. Функция
_print_byte
извлекается изoperator<<
С параметр функцииsize
, чтобы избежать создания экземпляра для разныхSize
.подробнее о двоичном коде bloat:
как уже упоминалось в улучшении 3, независимо от того, насколько широко
hex
иhex_sep
используется, только две копии (почти) дублированной функции будут выходить в двоичном коде:_print_byte<true>
и_print_byte<false>
. И вы могли бы понять, что это дублирование также может быть устранено с помощью точно такого же подхода: добавить параметр функцииsep
. Да, но если это так, время выполнения это. Мне нужна общая библиотечная утилита, которая может широко использоваться в программе, поэтому я скомпрометировал дублирование, а не накладные расходы во время выполнения. Я достиг этого с помощью время компиляцииif
: C++11std::conditional
накладные расходы на вызов функции могут быть оптимизированы до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)); } }
struct { int x; } output = {0xdeadbeef}; cout << hex_sep(output) << std::uppercase << hex(output) << endl;
выход:
de ad be ef DEADBEEF
Я понимаю, что это старый вопрос, но его также лучший результат Google в поиске решения очень похожей проблемы, которая у меня есть, а именно желание реализовать произвольные преобразования целых чисел в шестнадцатеричные строки в классе шаблона. Моя конечная цель была на самом деле
Gtk::Entry
шаблон подкласса, который позволит редактировать различные целочисленные ширины в шестнадцатеричном формате, но это не имеет значения.это сочетает в себе унарный
operator+
трюк сstd::make_unsigned
С<type_traits>
предотвратить проблему знак-расширение отрицательныйint8_t
илиsigned char
значения, которые встречаются в ответв любом случае, я считаю, что это более лаконично, чем любое другое общее решение. Он должен работать для любой целочисленные типы со знаком или без знака и выдает ошибку времени компиляции при попытке создать экземпляр функции с любыми нецелочисленными типами.
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(); }
пример использования:
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; }
обновление: альтернативно, если вы этого не сделаете как и сама идея
ostringstream
будучи используемым, вы можете объединить трюк с шаблоном и унарным оператором с решением на основе структуры принятого ответа для следующего. Обратите внимание, что здесь я изменил шаблон, удалив проверку для целочисленных типов. Элементmake_unsigned
использование может быть достаточно для гарантии безопасности типа времени компиляции.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;
Ну, это работает для меня:
std::cout << std::hex << (0xFF & a) << std::endl;
Если вы просто разыгрываете
(int)
как и предполагалось, он может добавить 1s слева отa
Если наиболее значимый бит равен 1. Таким образом, создание этого двоичного кода и операции гарантирует, что выход будет иметь левые биты, заполненные 0s, а также преобразует его в unsigned int, заставляя cout печатать его как hex.Я надеюсь, что это помогает.
Это также будет работать:
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; }
я использовал таким образом.
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;