как мне распечатать беззнаковый символ в шестнадцатеричном виде в С++, используя ostream?
-
21-08-2019 - |
Вопрос
Я хочу работать с беззнаковыми 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
Я попробовал использовать uint8_t
, но, как я уже упоминал ранее, это определено по типу unsigned char
, поэтому он делает то же самое.Как правильно распечатать переменные?
Редактировать: Я делаю это во многих местах моего кода.Могу ли я как-нибудь это сделать? без кастинг на int
каждый раз, когда я хочу распечатать?
Решение
Я бы предложил использовать следующую технику:
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 );
Редактировать: Однако решение Мартина Стеттнера намного лучше!
Подробнее об этом вы можете прочитать на 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/.Я публикую это только потому, что стало ясно, что автор вышеуказанных статей этого делать не собирается.
Самый простой и правильный метод печати символа в шестнадцатеричном виде:
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);
Версия для дайджеста читателей о том, как это работает, заключается в том, что унарный оператор + вызывает преобразование типа no op в int с правильной подписью.Таким образом, беззнаковый символ преобразуется в беззнаковое целое число, знаковый символ преобразуется в целое число, а символ преобразуется либо в беззнаковое целое число, либо в целое число, в зависимости от того, является ли символ знаковым или беззнаковым на вашей платформе (для многих это шокирует, что символ является особенным и не указано как подписанное или беззнаковое).
Единственный минус этой техники в том, что человеку, незнакомому с ней, может быть неочевидно, что происходит.Однако я считаю, что лучше использовать правильную технику и учить ей других, чем делать что-то неправильное, но более понятное.
Ну, это работает для меня:
std::cout << std::hex << (0xFF & a) << std::endl;
Если вы просто кастуете (int)
как было предложено, он может добавить 1 с слева от a
если его старший бит равен 1.Таким образом, выполнение этой двоичной операции И гарантирует, что левые биты вывода будут заполнены нулями, а также преобразует его в беззнаковое целое число, заставляя cout печатать его в шестнадцатеричном виде.
Надеюсь, это поможет.
Хм, кажется, я вчера велосипед заново изобрел...Но, по крайней мере, на этот раз это обычное колесо :) 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;
}
Я бы сделал это как 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;
}
Это может показаться излишним, но, как сказал Бьярне:«Библиотеки должны быть простыми в использовании, а не простыми в написании».
Я думаю, что ответ ТрунгТН и анона в порядке, но способ реализации функции hex(), предложенный Мартином Стеттнером, не очень прост и слишком темен, учитывая, что hex << (int)mychar уже является обходным путем.
вот мое решение, позволяющее упростить оператор «<<»:
#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;
}
Просто не стоит реализовывать потоковый оператор :-)
Я бы предложил:
std::cout << setbase(16) << 32;
Вы можете попробовать следующий код:
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
ссылочный тип.В коде @FredOverflowh.x >>= 4
меняет выводh
, который, как ни удивительно, несовместим со стандартной библиотекой, и введитеT
требуется, чтобы его можно было копировать. - Предположим только
CHAR_BITS
кратно 4.Код @FredOverflow предполагаетchar
является 8-битным, что не всегда верно, в некоторых реализациях на DSP, в частности, нередко случается, чтоchar
это 16-бит, 24-бит, 32-бит и т. д.
улучшать:
- Поддержка всех других манипуляторов стандартной библиотеки, доступных для целочисленных типов, например.
std::uppercase
.Поскольку вывод формата используется в_print_byte
, стандартные библиотечные манипуляторы по-прежнему доступны. - Добавлять
hex_sep
для печати отдельных байтов (обратите внимание, что в C/C++ «байт» по определению — это единица хранения размеромchar
).Добавить параметр шаблонаSep
и создать экземпляр_Hex<T, false>
и_Hex<T, true>
вhex
иhex_sep
соответственно. - Избегайте раздувания двоичного кода.Функция
_print_byte
извлекается изoperator<<
, с параметр функцииsize
, чтобы избежать создания экземпляров для разныхSize
.
Подробнее о раздувании двоичного кода:
Как упоминалось в улучшении 3, независимо от того, насколько широко hex
и hex_sep
используется, только две копии (почти) дублированной функции завершатся в двоичном коде: _print_byte<true>
и _print_byte<false>
.И вы, возможно, поняли, что это дублирование можно устранить, используя точно такой же подход:добавить параметр функции sep
.Да, но в этом случае среда выполнения if(sep)
необходим.Мне нужна общая библиотечная утилита, которую можно было бы широко использовать в программе, поэтому я пошел на компромисс в отношении дублирования, а не накладных расходов во время выполнения.Я добился этого, используя время компиляции if
:С++11 std::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::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;