如何使用ostream在c ++中打印unsigned char作为十六进制?

Nat*_*man 68 c++ formatting ostream

我想在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;
Run Code Online (Sandbox Code Playgroud)

然后输出是:

a is ^@; b is 377
Run Code Online (Sandbox Code Playgroud)

代替

a is 0; b is ff
Run Code Online (Sandbox Code Playgroud)

我尝试过使用uint8_t,但正如我之前提到的,那是typedef'ed unsigned char,所以它也是如此.如何正确打印变量?

编辑:我在我的代码中的许多地方都这样做.有没有什么办法可以做到这一点,而不铸造int我想打印每一次?

Mar*_*ner 51

我建议使用以下技术:

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;
}
Run Code Online (Sandbox Code Playgroud)

它的编写时间短,与原始解决方案具有相同的效率,它允许您选择使用"原始"字符输出.它是类型安全的(不使用"邪恶"宏:-))

  • 你的代码中有一个小错误.如果在代码中输入否定字符,十六进制值将变为4个字节而不是2.切换到"unsigned char"可以解决问题. (14认同)
  • 另一个错误(?)是上面的`operator <<`将给定流的模式改为十六进制:`cout << hex(a)<< 100`会给你一个惊喜.您应该在修改流之前存储流的状态,并在以后恢复它. (12认同)
  • 只要我们避开宏:为了更加完整的C++,你不应该把`(int)hs.c`写成`static_cast <int>(hs.c)`?:P (10认同)
  • 我真的很惊讶C++在将`char`转换为hex时有多糟糕. (3认同)

小智 47

使用:

cout << "a is " << hex << (int) a <<"; b is " << hex << (int) b << endl;
Run Code Online (Sandbox Code Playgroud)

如果你想要用前导零填充,那么:

#include <iomanip>
...
cout << "a is " << setw(2) << setfill('0') << hex << (int) a ; 
Run Code Online (Sandbox Code Playgroud)

由于我们正在使用C风格的演员阵容,为什么不用终端C++的糟糕程度来使用一个宏!

#define HEX( x )
   setw(2) << setfill('0') << hex << (int)( x )
Run Code Online (Sandbox Code Playgroud)

你可以说

cout << "a is " << HEX( a );
Run Code Online (Sandbox Code Playgroud)

编辑:话虽如此,MartinStettner的解决方案更好!

  • 我可以说服你一劳永逸地避开邪恶的C式演员阵容吗?http://stackoverflow.com/questions/527999/is-it-ok-to-use-c-style-cast-for-built-in-types/528009#528009 (4认同)

ltc*_*ltc 31

您可以在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);
Run Code Online (Sandbox Code Playgroud)

读者摘要版本的工作原理是一元+运算符强制将no op类型转换为具有正确签名的int.因此,unsigned char转换为unsigned int,signed char转换为int,char将转换为unsigned int或int,具体取决于char是否在您的平台上签名或签名(对于很多char来说,这是特别令人震惊的并且未指定为有符号或无符号).

这种技术的唯一不利之处在于,对于不熟悉它的人来说可能并不明显.但是,我认为最好使用正确的技术并教导其他人,而不是做一些不正确但更直接清楚的事情.

  • @BrianMcFarland我注意到这个问题甚至对于unsigned char(当大于127时),所以我用一个位掩码解决了它:`(+ c&0xFF)`. (5认同)
  • 为什么这不是最佳答案?它简短而有效. (3认同)
  • @To마SE,一元“+”是不必要的,二进制“&amp;”已经转换为“int”。 (3认同)
  • 这种技术对符号扩展的类型小于`unsigned int` /`int`的类型,这意味着对于负符号字符(例如int16_t),您将获得8个半字节的输出,其中前6个为F。 (2认同)

vit*_*aut 17

在 C++20 中,您将能够使用它std::format来执行此操作:

std::cout << std::format("a is {:x}; b is {:x}\n", a, b);
Run Code Online (Sandbox Code Playgroud)

输出:

std::cout << std::format("a is {:x}; b is {:x}\n", a, b);
Run Code Online (Sandbox Code Playgroud)

在此期间,您可以使用的{} FMT库std::format是基于。{fmt} 还提供了print使这更容易和更高效的功能(Godbolt):

fmt::print("a is {:x}; b is {:x}\n", a, b);
Run Code Online (Sandbox Code Playgroud)

免责声明:我是 {fmt} 和 C++20 的作者std::format


Vin*_*cia 7

好吧,这对我有用:

std::cout << std::hex << (0xFF & a) << std::endl;
Run Code Online (Sandbox Code Playgroud)

如果(int)按照建议的方式a进行强制转换,则它的最高有效位为1时,可能会在其左侧添加1。因此,执行此二进制AND操作可确保输出的左侧位由0填充,并将其转换为unsigned int,从而强制cout打印它作为十六进制。

我希望这有帮助。

  • 这是最简单和最干净的方法。 (3认同)

tdi*_*ihp 5

我认为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;
}
Run Code Online (Sandbox Code Playgroud)

只是不值得实现流运算符:-)


fre*_*low 5

嗯,看来我昨天重新发明了轮子……但是,至少这次是通用轮子了:)chars印有两个十六进制数字,shorts 印有四个十六进制数字,依此类推。

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢。简洁。不需要多余的信息。我希望通过布尔来表示是否需要大写或小写。或尊重[std :: uppercase`](http://en.cppreference.com/w/cpp/io/manip/uppercase)和[`std :: nouppercase`](http://en.cppreference。 com / w / cpp / io / manip / nouppercase)操纵器(可操纵[`std :: ios_base :: flags :: uppercase`]](http://en.cppreference.com/w/cpp/io/ios_base/标志)iosflag) (2认同)