如何使用cout输出0x0a而不是0xa?

84

如何使用cout打印0x0a而不是0xa?

#include  <iostream>

using std::cout;  
using std::endl;  
using std::hex;

int main()  
{  
    cout << hex << showbase << 10 << endl;  
}

您的具体要求是什么?标题中写着“0x0a”,而您的问题正文中写着“0x0A”。无论哪种方式,显式输出std::cout << "0x0a"或者std::cout << "0x0A"都能够满足您的需求。但我猜想您真正想做的是格式化数字。您希望对数字如16、255、256、65536、-1进行怎样的格式化呢? - CB Bailey
@Charles 这个问题是关于如何在 x 和 A(或者 a)之间打印 0 的。你可以验证第一个答案是错误的,因为它仍然打印出 0xa(如果使用大写,则为 0xA)。但无论如何,这个问题已经被标记为已回答。 - Ayrosa
但是为什么要多一个 0 呢?你总是需要 0x0 吗,还是你需要字符串的最小长度? - CB Bailey
如果十六进制数是0xA,我希望它是0x0A。如果数字是0x123,我希望它是0x0123,依此类推,即具有偶数个十六进制数字。 - Ayrosa
我认为你没有理解我的意思。你已经明确指定了10的显示方式,但是1到9和11到15呢?256呢?你想要0x0a是因为你想要最小宽度还是因为你总是想要前导零?除非你的规格说明非常准确,否则你可能得不到想要的答案。 - CB Bailey
9个回答

139

这对我在GCC中有效:

#include  <iostream>
#include  <iomanip>

using namespace std;

int main()
{
    cout << "0x" << setfill('0') << setw(2) << right << hex << 10 << endl;
}

如果你已经对iostream的格式化怪癖感到厌烦了,那么试试Boost.Format。它允许使用老式的printf风格的格式说明符,但它是类型安全的。

#include <iostream>
#include <boost/format.hpp>

int main()
{
    std::cout << boost::format("0x%02x\n") % 10;
}

更新(2019年)

请查看已经被接受进入C++20的 {fmt}库。基准测试显示它比 Boost.Format 更快。

#if __has_include(<format>)
    #include <format>
    using std::format;
#else
    #include <fmt/format.h>
    using fmt::format;
#endif

std::cout << format("{:#04x}\n", 10);

这个答案是错误的。如果之前有人在输出前使用了 cout << left << whatever,那么由于持久性的左/右重定向,你会得到错误的输出结果。必须使用 setfill('0') << setw(2) << right << hex 来始终确保正确对齐。 - fuujuhi
2
@fuujuhi 错了?那太严厉了。我会说它是不完整的。不过还是谢谢你的建议。 - Emile Cormier
3
@EmileCornier,不好意思,我的话可能有点严厉。确实存在不完整的情况,但不幸的是它可能会导致严重的错误。最近我的代码中出现了两个因为持久的 iomanip 而引起的错误(全面披露:我曾经经常编写 C++ 代码,但现在已经不再)。这让我认为 C++ 的 iostream 简直是错到家了。对于一门现代化的语言来说,iostream 让打印最简单的内容成为一场噩梦。你认为你要打印一个整数,但实际上你打印的是十六进制数。或者你的输出中得到了“0x90”,但实际上这个值是0x09。 - fuujuhi

26

使用 iomanip 中的 setwsetfill

#include  <iostream>
#include  <iomanip>

using std::cout;  
using std::endl;  
using std::hex;

int main()
{
    cout << "0x" << std::setfill('0') << std::setw(2) << hex << 10 << endl;
}

就我个人而言,iostream的有状态特性总是让我感到烦恼。我认为boost format是一个更好的选择,所以我建议采用其他答案。


7
我也得到了0xA。为什么用iostream让输出变成想要的格式这么难? - Emile Cormier
2
这对我有用:cout << "0x" << setfill('0') << setw(2) << hex << 10 << endl - Emile Cormier
1
@Doug T:我擅自编辑了您的答案,以便OP可以接受它作为有效答案。 - Emile Cormier
4
@Charles: 抱歉,我忘记了加上setiosflags(ios::internal)。这个代码可以正常运行:cout << showbase << setiosflags(ios::internal) << setfill('0') << setw(4) << hex << 10 << endl; 示例:http://ideone.com/WlusB - Potatoswatter
1
请注意,在每个“<< n”之前可能需要调用setw,至少对我来说是这样。 - kraxor
显示剩余7条评论

9
如果你想更容易地输出十六进制数,你可以编写以下函数:

下面是更新版本,有两种方法可以插入0x基础指示符,并在脚注中详细说明它们之间的区别。为了不给使用原版的人带来不便,原版保留在答案底部。

请注意,更新和原始版本都可能需要适应字节大小为9位倍数的系统。

#include <type_traits> // For integral_constant, is_same.
#include <string>      // For string.
#include <sstream>     // For stringstream.
#include <ios>         // For hex, internal, [optional] showbase.
                       // Note: <ios> is unnecessary if <iostream> is also included.
#include <iomanip>     // For setfill, setw.
#include <climits>     // For CHAR_BIT.

namespace detail {
    constexpr int HEX_DIGIT_BITS = 4;
    //constexpr int HEX_BASE_CHARS = 2; // Optional.  See footnote #2.

    // Replaced CharCheck with a much simpler trait.
    template<typename T> struct is_char
      : std::integral_constant<bool,
                               std::is_same<T, char>::value ||
                               std::is_same<T, signed char>::value ||
                               std::is_same<T, unsigned char>::value> {};
}

template<typename T>
std::string hex_out_s(T val) {
    using namespace detail;

    std::stringstream sformatter;
    sformatter << std::hex
               << std::internal
               << "0x"                                             // See footnote #1.
               << std::setfill('0')
               << std::setw(sizeof(T) * CHAR_BIT / HEX_DIGIT_BITS) // See footnote #2.
               << (is_char<T>::value ? static_cast<int>(val) : val);

    return sformatter.str();
}

它可以使用以下方式:

uint32_t       hexU32 = 0x0f;
int            hexI   = 0x3c;
unsigned short hexUS  = 0x12;

std::cout << "uint32_t:       " << hex_out_s(hexU32) << '\n'
          << "int:            " << hex_out_s(hexI)   << '\n'
          << "unsigned short: " << hex_out_s(hexUS)  << std::endl;

请参见下文详细说明的两个选项:此处可实时查看。

注释:

  1. This line is responsible for showing the base, and can be either of the following:

    << "0x"
    << std::showbase
    
    • The first option will display improperly for custom types that try to output negative hex numbers as -0x## instead of as <complement of 0x##>, with the sign displaying after the base (as 0x-##) instead of before it. This is very rarely an issue, so I personally prefer this option.

      If this is an issue, then when using these types, you can check for negativity before outputting the base, then using abs() (or a custom abs() that returns an unsigned value, if you need to be able to handle the most-negative values on a 2's complement system) on val.

    • The second option will omit the base when val == 0, displaying (e.g., for int, where int is 32 bits) 0000000000 instead of the expected 0x00000000. This is due to the showbase flag being treated like printf()'s # modifier internally.

      If this is an issue, you can check whether val == 0, and apply special handling when it does.

  2. Depending on which option was chosen for showing the base, two lines will need to be changed.

    • If using << "0x", then HEX_BASE_CHARS is unnecessary, and can be omitted.
    • If using << std::showbase, then the value supplied to setw() needs to account for this:

      << std::setw((sizeof(T) * CHAR_BIT / HEX_DIGIT_BITS) + HEX_BASE_CHARS)
      
抱歉,我只能理解和回答英文问题。如果您有英文相关的问题或需要翻译,请告诉我。
// Helper structs and constants for hex_out_s().
namespace hex_out_helper {
    constexpr int HEX_DIGIT_BITS = 4; // One hex digit = 4 bits.
    constexpr int HEX_BASE_CHARS = 2; // For the "0x".

    template<typename T> struct CharCheck {
        using type = T;
    };

    template<> struct CharCheck<signed char> {
        using type = char;
    };

    template<> struct CharCheck<unsigned char> {
        using type = char;
    };

    template<typename T> using CharChecker = typename CharCheck<T>::type;
} // namespace hex_out_helper


template<typename T> std::string hex_out_s(T val) {
    using namespace hex_out_helper;

    std::stringstream sformatter;
    sformatter << std::hex
               << std::internal
               << std::showbase
               << std::setfill('0')
               << std::setw((sizeof(T) * CHAR_BIT / HEX_DIGIT_BITS) + HEX_BASE_CHARS)
               << (std::is_same<CharChecker<T>, char>{} ? static_cast<int>(val) : val);
    return sformatter.str();
}

然后可以像这样使用:

uint32_t       hexU32 = 0x0f;
int            hexI   = 0x3c;
unsigned short hexUS  = 0x12;

std::cout << hex_out_s(hexU32) << std::endl;
std::cout << hex_out_s(hexI) << std::endl;
std::cout << "And let's not forget " << hex_out_s(hexUS) << std::endl;

示例: 这里提供。


4
std::internal! 你赢得了大奖!这就是我所缺少的!谢谢 @Justin Time。 - j4x
@fljx 不用谢。自从我发布这个答案以来,我已经对它进行了改进,所以我会编辑答案以反映这一点。 - Justin Time - Reinstate Monica
所需的包含应该在某个地方列出,从我浏览的内容来看:climitsiomanipiostreamtypesstringstringstream,其中 climits 中的 CHAR_BIT 引发了我写下这条评论。 - mxmlnkn
好主意,@mxmlnkn。已更新。(另外,从技术上讲,它需要使用ios而不是iostream,但这是无关紧要的,因为iostream必须包含ios。已在答案中注明。) - Justin Time - Reinstate Monica
请注意,这可能需要进行修改以支持C++20的char8_t,并且为了方便起见,将所有字符类型([[un]signed] charcharN_twchar_t)视为相同可能也很有用。 在这种情况下,当is_char<T>为真时,最好将其转换为char_traits<T>::int_type,因为我们已经使用了<string>。 [不过,我目前正在等待事情的进展。] - Justin Time - Reinstate Monica

6
在 C++20 中,您将能够使用 std::format 来实现此功能:
std::cout << std::format("{:02x}\n", 10);  

输出:

0a

与此同时,您可以使用 {fmt}库std::format是基于它的。{fmt}还提供了print函数,使这一过程更加简便和高效(godbolt):
fmt::print("{:02x}\n", 10); 

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


3
重要的是,答案中遗漏的一点是你必须在上述所有标志中使用right
cout<<"0x"<<hex<<setfill('0')<<setw(2)<<right<<10;

1
或者,如果您使用的是std::showbase而不是直接输出0x,则应使用std::internal而不是std::right - Justin Time - Reinstate Monica

1

试试这个...你只需根据数量级在前面添加零。

cout << hex << "0x" << ((c<16)?"0":"") << (static_cast<unsigned int>(c) & 0xFF) << "h" << endl;

你可以轻松地修改它以处理更大的数字。
cout << hex << "0x";
cout << ((c<16)?"0":"") << ((c<256)?"0":"");
cout << (static_cast<unsigned int>(c) & 0xFFF) << "h" << endl;

因子为16(对于一个十六进制数字):
16、256、4096、65536、1048576等
分别为
0x10、0x100、0x1000、0x10000、0x100000等
因此,你也可以这样写。
cout << hex << "0x" << ((c<0x10)?"0":"") << ((c<0x100)?"0":"") << ((c<0x1000)?"0":"") << (static_cast<unsigned int>(c) & 0xFFFF) << "h" << endl;

And so on.. :P


1
为了缩短输出十六进制的内容,我创建了一个简单的宏。
#define PADHEX(width, val) setfill('0') << setw(width) << std::hex << (unsigned)val

那么

cout << "0x" << PADHEX(2, num) << endl;

我微笑了。编写C++有时可能非常繁琐,你不得不求助于禁忌领域,比如宏定义 ("他说宏定义,他说宏定义","杀了他,杀了他" ... 《布莱恩的人生》为C++重新想象)以保持可读性。 - 0xC0000022L

0

将任何数字以十六进制形式打印出来,并自动填充“0”或设置。模板允许使用任何数据类型(例如uint8_t)

template<typename T, typename baseT=uint32_t> struct tohex_t {
    T num_;
    uint32_t width_;
    bool showbase_;

    tohex_t(T num, bool showbase = false, uint32_t width = 0) { num_ = num; showbase_ = showbase; width_ = width; }
    friend std::ostream& operator<< (std::ostream& stream, const tohex_t& num) {
        uint32_t w;
        baseT val;

        if (num.showbase_)
            stream << "0x";

        if (num.width_ == 0) {
            w = 0;
            val = static_cast<baseT>(num.num_);
            do { w += 2; val = val >> 8; } while (val > 0);
        }
        else {
            w = num.width_;
        }
        stream << std::hex << std::setfill('0') << std::setw(w) << static_cast<baseT>(num.num_);

        return stream;
    }
};
template<typename T> tohex_t<T> TO_HEX(T const &num, bool showbase = false, uint32_t width = 0) { return tohex_t<T>(num, showbase, width); }

例子:

std::stringstream sstr;
uint8_t ch = 91;
sstr << TO_HEX(5) << ',' << TO_HEX(ch) << ',' << TO_HEX('0') << std::endl;
sstr << TO_HEX(1, true, 4) << ',' << TO_HEX(15) << ',' << TO_HEX(-1) << ',';
sstr << TO_HEX(513) << ',' << TO_HEX((1 << 16) + 3, true);
std::cout << sstr.str();

输出:

05,5b,30
0x0001,0f,ffffffff,0201,0x010003

0
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
    /*This should print out 0x0a. The internal adjustment pads the width with the fill character*/
    cout << hex << showbase << internal << setfill('0') << setw(4) << 10 << endl;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接