как напечатать беззнаковый символ как шестнадцатеричный в 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 54

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

Я предлагаю:

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

взяты из: http://www.cprogramming.com/tutorial/iomanip.html

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

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++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::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;