Как я могу напечатать беззнаковый символ как шестнадцатеричный в C ++, используя Ostream? - PullRequest
64 голосов
/ 23 марта 2009

Я хочу работать с 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, но, как я упоминал ранее, это typedef 'to unsigned char, поэтому он делает то же самое. Как правильно распечатать мои переменные?

Редактировать: Я делаю это во многих местах моего кода. Есть ли способ сделать это без приведения к int каждый раз, когда я хочу напечатать?

Ответы [ 14 ]

49 голосов
/ 23 марта 2009

Я бы предложил использовать следующую технику:

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

Он короток для написания, имеет ту же эффективность, что и оригинальное решение, и позволяет выбрать «оригинальный» вывод символов. И это безопасно для типов (без использования «злых» макросов: -))

44 голосов
/ 23 марта 2009

Использование:

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

Редактировать: Сказав это, решение MartinStettner гораздо приятнее!

30 голосов
/ 06 февраля 2015

Подробнее об этом можно прочитать на 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);

Дайджест версии читателя о том, как это работает, состоит в том, что унарный оператор + вызывает преобразование типа без операции в int с правильной подписью. Таким образом, unsigned char преобразуется в unsigned int, подписанный char преобразуется в int, а char преобразуется в unsigned int или int в зависимости от того, является ли char подписанным или неподписанным на вашей платформе (для многих становится шоком, что char особенный и не указывается как подписанный или неподписанный).

Единственный минус этой техники в том, что может быть не очевидно, что происходит с кем-то, кто не знаком с ней. Тем не менее, я думаю, что лучше использовать правильную технику и учить других этому, чем делать что-то неправильное, но более понятное.

5 голосов
/ 04 сентября 2013

Хм, кажется, я вчера заново изобрел колесо ... Но эй, по крайней мере, это универсальное колесо на этот раз :) 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;
}
4 голосов
/ 01 марта 2018

Ну, это работает для меня:

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

Если вы просто приведете (int) в соответствии с предложением, оно может добавить 1 с слева от a, если его старший бит равен 1. Таким образом, выполнение этой двоичной операции И гарантирует, что на выходе будут заполнены левые биты нулями, а также преобразует его в unsigned int, заставляя cout печатать его в шестнадцатеричном виде.

Надеюсь, это поможет.

4 голосов
/ 28 сентября 2012

Я думаю, что TrungTN и anon ответ в порядке, но способ реализации функции hex () от MartinStettner не очень простой и слишком мрачный, учитывая, что hex

вот мое решение, чтобы упростить оператор "<<": </p>

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

Просто не стоит реализовывать оператор потока: -)

4 голосов
/ 23 марта 2009

Я бы сделал это как 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;
}

Это может показаться излишним, но, как сказал Бьярне: «библиотеки должны быть простыми в использовании, а не простыми в написании».

2 голосов
/ 16 мая 2016

Я использую следующее в 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();
}
2 голосов
/ 07 мая 2012

Вы можете попробовать следующий код:

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

2 голосов
/ 02 февраля 2012

Я бы предложил:

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

Взято из: http://www.cprogramming.com/tutorial/iomanip.html

...