如何使用ostream在c ++中打印unsigned char作为十六进制?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用ostream在c ++中打印unsigned char作为十六进制?相关的知识,希望对你有一定的参考价值。
我想在C ++中使用无符号的8位变量。就算术而言,unsigned char
或uint8_t
都可以做到这一点(这是预期的,因为AFAIK uint8_t
只是unsigned char
的别名,或者调试器提出它。
问题是,如果我在C ++中使用ostream打印出变量,它会将其视为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;
}
它的编写时间短,与原始解决方案具有相同的效率,它允许您选择使用“原始”字符输出。它是类型安全的(不使用“邪恶”宏:-))
我在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<<
的Rhs应该是const
参考类型。在@ FredOverflow的代码中,h.x >>= 4
更改了输出h
,这与标准库出乎意料地不兼容,并且类型T
被重新构造为可复制构造。- 假设只有
CHAR_BITS
是4的倍数。@ FredOverflow的代码假设char
是8位,在DSP的某些实现中并不总是如此,特别是,char
是16位,24位,32-并不罕见比特等
提高:
- 支持可用于整数类型的所有其他标准库操纵器,例如
std::uppercase
。由于格式输出在_print_byte
中使用,标准库操纵器仍然可用。 - 添加
hex_sep
以打印单独的字节(请注意,在C / C ++中,'byte'根据定义是一个大小为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
实现了这一点: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
我意识到这是一个老问题,但它也是搜索解决我遇到的非常类似问题的解决方案的最佳结果,这是在模板类中实现任意整数到十六进制字符串转换的愿望。我的最终目标实际上是一个Gtk::Entry
子类模板,它允许以十六进制编辑各种整数宽度,但这不是重点。
这结合了一元operator+
技巧与来自std::make_unsigned
的<type_traits>
,以防止在int8_t
发生的符号扩展负signed char
或this answer值的问题
无论如何,我相信这比任何其他通用解决方案都更简洁。它应该适用于任何有符号或无符号整数类型,如果尝试使用任何非整数类型实例化该函数,则会抛出编译时错误。
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("
%s",chResut);
delete chResut;
chResut = NULL;
使用:
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的解决方案更好!