我如何使用ostream在c ++中将未签名的char打印为十六进制?


74

我想在C ++中使用无符号的8位变量。无论是unsigned charuint8_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,但是正如我之前提到的,这是typedefeded的unsigned char,所以也一样。如何正确打印变量?

编辑:我在代码中的很多地方都这样做。有什么方法可以在强制int每次打印的情况下执行此操作?


2
我认为MartinStettner的答案相当混乱,我认为不应该实现额外的结构和额外的流运算符。anon的解决方案很简单,对我来说足够好。
tdihp 2012年

Answers:


55

我建议使用以下技术:

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

它写起来短,效率与原始解决方案相同,它使您可以选择使用“原始”字符输出。而且它是类型安全的(不使用“邪恶”宏:-)


11
只要我们避开宏:为了更完整地使用C ++,您是否应该编写(int)hs.cstatic_cast<int>(hs.c)?:P
塞斯·约翰逊

14
您的代码中有一个小错误。如果在代码中输入负号,则十六进制值将变为4个字节,而不是2个字节。切换unsigned char到此可解决此问题。
Ted

12
另一个错误(?)是上面operator<<的方法将给定流的模式更改为十六进制:cout << hex(a) << 100会让您感到惊讶。您应该先存储流的状态,然后再对其进行修改,并在以后还原它。
musiphil 2012年

5
我真的很惊讶C ++转换char为十六进制有多么糟糕。
克雷格·林格

51

采用:

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的解决方案要好得多!


4
我能说服您一劳永逸地避开邪恶的C型演员表吗?stackoverflow.com/questions/527999/...
康拉德·鲁道夫

1
在这种情况下不是-这是我认为唯一有理由的地方。

我会这样做,只是要避免强制转换,我会使用cout << hex << int(a); 它与演员表具有相同的含义,没有演员表。:)
Brian Neal

35

您可以在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-character-the-character-的数字值。我之所以仅发布此内容,是因为上述文章的作者已明确不打算这样做。

最简单,最正确的将字符打印为十六进制的方法是

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。因此,一个无符号的char转换为unsigned int,一个有符号的char转换为int,一个char转换为unsigned int或int,这取决于char是在平台上是带符号的还是无符号的(这让很多人感到震惊,因为char是特殊的并且未指定为已签名或未签名)。

该技术的唯一缺点是,对于不熟悉该技术的人来说,发生的事情可能并不明显。但是,我认为最好使用正确的技术并向其他人传授有关知识,而不是做一些不正确但更直接的事情。


3
为什么这不是最佳答案?它简短有效。
James Pack

2
此技术对小于unsigned int/的类型进行符号扩展int,这意味着对于负号字符(例如int16_t),您将获得8个半字节的输出,其中前6个为F
Brian McFarland

5
@BrianMcFarland我注意到这个问题,即使对于未签名的char(大于127)也是如此,所以我用位掩码解决了这个问题(+c & 0xFF)
To마SE

@To마SE,一进制+是不必要的,二进制文件&已经转换为int
阿德里安

18

好吧,这对我有用:

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

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

我希望这有帮助。


3
这是一堆最简单,最干净的方法。
阿德里安

我也很喜欢这个答案!我用了这个解决方案。
九月

13

在C ++ 20中,您将可以std::format执行以下操作:

std::cout << std::format("a is {:x}; b is {:x}\n", a, b);

输出:

a is 0; b is ff

在此期间,您可以使用的{} FMT库std::format是基于。{fmt}还提供了print使此操作更加轻松和高效的功能(godbolt):

fmt::print("a is {:x}; b is {:x}\n", a, b);

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


6

嗯,看来我昨天重新发明了轮子……但是,至少这次是通用轮子了:)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;
}

2
我喜欢。简洁。不需要多余的信息。我希望通过布尔来表示是否需要大写或小写。或纪念std::uppercasestd::nouppercase操纵者(操纵std::ios_base::flags::uppercaseiosflag)
sehe

4

我认为TrungTN和anon的答案还可以,但是考虑到hex <<(int)mychar已经是一种解决方法,MartinStettner的实现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;
}

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


3

我会像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;
}

看起来似乎有些矫but过正,但是正如Bjarne所说:“图书馆应该易于使用,而不是易于编写”。



2

您可以尝试以下代码:

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


尽管这确实可以解决问题,但我的要求之一是不必在int每次执行此操作时都强制转换,因为这在代码中出现了太多次。
内森·费尔曼

2

我在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

我认为我们缺少有关这些类型转换如何工作的解释。

char取决于平台signedunsigned。在x86char中等效于signed char

当为整数类型(时charshortintlong)转化为一个较大容量的类型,转化是通过添加零到由左侧的情况下,unsigned类型和通过符号扩展为signed那些。符号扩展包括将原始数字的最高有效(最左边)位复制到左侧,直到达到目标类型的位大小为止。

因此,如果我在signed char默认系统中并且执行此操作:

char a = 0xF0; // Equivalent to the binary: 11110000
std::cout << std::hex << static_cast<int>(a);

F...F0由于领先地位1得到了扩展,我们将获得。

如果要确保仅F0在任何系统中打印,则必须将其他中间类型强制转换为unsigned char以便添加零,并且由于它们对于只有8位的整数不重要,因此不打印:

char a = 0xF0; // Equivalent to the binary: 11110000
std::cout << std::hex << static_cast<int>(static_cast<unsigned char>(a));

这产生 F0


1

我意识到这是一个古老的问题,但它也是Google寻求与我非常相似的问题的解决方案时的最佳结果,这是希望在模板类中实现任意整数到十六进制字符串的转换。我的最终目标实际上是Gtk::Entry子类模板,该模板将允许以十六进制编辑各种整数宽度,但这不重要。

这结合了一元operator+技巧和std::make_unsignedfrom<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;

0

我想发布基于@FredOverflow的重新发明版本。我进行了以下修改。

固定:

  • Rhsoperator<<应该是const参考类型。在@FredOverflow的代码中,h.x >>= 4更改了output 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>hexhex_sep分别。
  • 避免二进制代码膨胀。函数_print_byteoperator<<通过函数参数 提取出来的,size避免对不同的实例化Size

有关二进制代码膨胀的更多信息:

如改进3所述,无论使用hexhex_sep使用的广泛程度,只有(几乎)重复函数的两个副本将以二进制代码退出:_print_byte<true>_print_byte<false>。您可能已经意识到,也可以使用完全相同的方法消除这种重复:添加一个function参数sep。是的,但是如果这样做,if(sep)则需要运行时。我想要一个可以在程序中广泛使用的通用库实用程序,因此我在复制而不是运行时开销方面妥协了。我是通过使用compile-time来实现的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


0

如果您使用的是预填充字符和签名字符,请注意不要在不需要的'F'后面附加

char out_character = 0xBE; cout << setfill('0') << setw(2) << hex << unsigned short(out_character);

印刷品:ffbe

在ffffffbe中使用int而不是short结果

为了防止不必要的f,您可以轻松屏蔽掉它们。

char out_character = 0xBE; cout << setfill('0') << setw(2) << hex << unsigned short(out_character) & 0xFF;


-2

这也将起作用:

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

但是,如果他实际上希望将强制转换输出为字符,则必须使用强制转换,这似乎有点不自然!

-2

我用这种方式。

    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;

1
这是关于C ++和std :: ostream的问题,而不是关于C和printf的问题。:)
daminetreg 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.