如何使用 ostream 在 C++ 中将 unsigned char 打印为十六进制?
-
21-08-2019 - |
题
我想在 C++ 中使用无符号 8 位变量。任何一个 unsigned char
或者 uint8_t
就算术而言,做这个技巧(这是预期的,因为据我所知 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
, ,但正如我之前提到的,这是 typedef'ed 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 );
编辑: 话虽如此,MartinStettner 的解决方案要好得多!
您可以在以下位置阅读有关此内容的更多信息 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,signed char 转换为 int,char 转换为 unsigned int 或 int,具体取决于 char 在您的平台上是有符号的还是无符号的(令许多人感到震惊的是 char 是特殊的并且未指定为有符号或无符号)。
这种技术的唯一缺点是,对于不熟悉该技术的人来说,发生的事情可能并不明显。然而,我认为最好使用正确的技术并教给其他人,而不是做一些不正确但更直接清晰的事情。
嗯,这对我有用:
std::cout << std::hex << (0xFF & a) << std::endl;
如果你只是投 (int)
正如所建议的,它可能会在左侧添加 1 a
如果它的最高有效位是 1。因此,进行这个二进制 AND 运算可以保证输出的左侧位被 0 填充,并将其转换为 unsigned int ,强制 cout 将其打印为十六进制。
我希望这有帮助。
嗯,看来我昨天又重新发明了轮子……但是,嘿,至少这次是通用轮子:) char
s 打印有两个十六进制数字, short
具有 4 个十六进制数字的 s 等等。
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;
}
这似乎有点矫枉过正,但正如 Bjarne 所说:“库应该易于使用,而不是易于编写”。
我认为TrungTN和anon的答案还可以,但是MartinStettner实现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;
}
只是不值得实现流运算符:-)
您可以尝试以下代码:
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
要求是可复制构造的。 - 仅假设
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
:C++11 std::conditional
, ,函数调用的开销有望通过以下方式优化掉 inline
.
十六进制打印.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"
十六进制打印.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
出现在的值 这个答案
无论如何,我相信这比任何其他通用解决方案都更简洁。它应该适用于 任何 有符号或无符号整数类型,如果尝试使用任何非整数类型实例化该函数,则会引发编译时错误。
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;