使用std::cout打印零填充的十六进制数

17

假设我有一个dword数值想要用std::cout输出16进制格式并且左侧补零,以便0xabcd被显示为0x0000abcd。看起来你需要这样做:

uint32_t my_int = 0xabcd;

std::cout << "0x" << std::hex << std::setw(8) << std::setfill('0')
    << my_int << std::endl;

使用C语言中的printf("0x%08X\n", my_int);即可轻松完成此操作,因此这似乎有些荒唐。除了使用using namespace std;之外,是否有其他方法可以更短地使用std::cout输出?


4
编写一个函数以格式化输出十六进制,并调用它。 - GManNickG
1
“短”是一种相对的概念,我想。如果你认为printf()背后的代码很短,那我可以向你保证它绝非如此。你只需自己编写(看起来与这里的代码极为相似)。您可以编写一个操作符对/操作类,允许像std::cout << as_hex(n,8,0);这样的输出。 - WhozCraig
为了让代码更简洁,我会使用宏来隐藏丑陋的部分,例如 #define PAD_HEX(digits) std::hex << std::setw(digits) << std::setfill('0'),然后在代码中稍后使用它,如 std::cout << "0x" << PAD_HEX(8) << my_int .. - erco
3个回答

17

我想您可以编写一个“流操作器”。如果您有多个十六进制数字要以该格式打印,这会很有用。这显然不是一种理想的解决方案,但是使用包装类型,您可以创建自己的“格式标志”来切换它。有关更多信息,请参见Sticky custom stream manipulator

#include <iostream>
#include <iomanip>

static int const index = std::ios_base::xalloc();

std::ostream& hexify(std::ostream& stream) {
    stream.iword(index) = 1;
    return stream;
}

std::ostream& nohexify(std::ostream& stream) {
    stream.iword(index) = 0;
    return stream;
}

struct WrapperType {
    uint32_t _m;
public:
    WrapperType(uint32_t m) : _m(m)
    {
    }

    uint32_t getm() const
    {
        return _m;
    }
};
std::ostream& operator<< (std::ostream& os, const WrapperType& t) {
    if (os.iword(index))
        return os << "0x" << std::hex << std::setw(8) << std::setfill('0') << t.getm();
    else
        return os << t.getm();
}

int main()
{
    WrapperType my_int{0xabcd};
    std::cout << hexify << my_int << my_int;
    std::cout << nohexify << my_int;
}

1
+1,这完全是我的做法,但是宽度操作器在每次向流 operator << 发送时都会被重置(据我记得),所以您可能需要将其作为参数化操作器的一部分。我可能错了,但似乎就是这样运作的(或者相反,我现在有阅读障碍)。编辑:刚刚测试,你是完全正确的。我刚才眼花了。抱歉造成了困惑。 - WhozCraig
@WhozCraig 我编辑了我的答案,但我不确定它是否更好。我的意思是,为了格式化目的而记住使用“WrapperType”可能很烦人,这就是你提供的只使用函数的解决方案更好的地方。但如果OP懒得话,也许这样可以节省打字。 - user1508519

5
我不会更改流的(全局)标志,只能更改操作器:
#include <iostream>
#include <iomanip>
#include <limits>

template <typename T>
struct Hex
{
    // C++11:
    // static constexpr int Width = (std::numeric_limits<T>::digits + 1) / 4;
    // Otherwise:
    enum { Width = (std::numeric_limits<T>::digits + 1) / 4 };
    const T& value;
    const int width;

    Hex(const T& value, int width = Width)
    : value(value), width(width)
    {}

    void write(std::ostream& stream) const {
        if(std::numeric_limits<T>::radix != 2) stream << value;
        else {
            std::ios_base::fmtflags flags = stream.setf(
                std::ios_base::hex, std::ios_base::basefield);
            char fill = stream.fill('0');
            stream << "0x" << std::setw(width) << value;
            stream.fill(fill);
            stream.setf(flags, std::ios_base::basefield);
        }
    }
};

template <typename T>
inline Hex<T> hex(const T& value, int width = Hex<T>::Width) {
    return Hex<T>(value, width);
}

template <typename T>
inline std::ostream& operator << (std::ostream& stream, const Hex<T>& value) {
    value.write(stream);
    return stream;
}

int main() {
    std::uint8_t u8 = 1;
    std::uint16_t u16 = 1;
    std::uint32_t u32 = 1;
    std::cout << hex(unsigned(u8), 2) << ", " << hex(u16) << ", " << hex(u32) << '\n';
}

我更喜欢这种方法。虽然我不确定数学上的比较如何,但我也更喜欢默认宽度的计算方式为:(sizeof(T)* 2)(每个字节2个十六进制字符)。这对我来说似乎更简单。 - BTownTKD

1

4
这更适合作为评论。 - alk

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