简单打印不同格式的数字

之前的章节中,我们已经了解如何打印出带格式的输出,同时也意识到了两点:

  • 输入输出控制符是有粘性的,所以当我们要临时使用的时候,需要在用完之后进行还原。

  • 其控制符和较少的要打印的对象相比,会显得很冗长。

这些原因导致一些开发者使用C++的时候,还是依旧使用printf进行打印输出。

本节,我们将来看一下如何不用太多代码就能进行很好的类型打印。

How to do it...

我们会先来实现一个类format_guard,其会自动的将打印格式进行恢复。另外,我们添加了一个包装类型,其可以包含任意值,当对其进行打印时,其能使用相应的格式进行输出,而无需添加冗长的控制符:

  1. 包含必要的头文件,并声明所使用的命名空间。

    #include <iostream>
    #include <iomanip>
    
    using namespace std;
  2. 辅助类在调用format_guard时,其会对输出流的格式进行清理。其构造函数保存了格式符,也就是在这里对std::cout进行设置。析构函数会将这些状态进行去除,这样就不会后续的打印有所影响:

    class format_guard {
        decltype(cout.flags()) f {cout.flags()};
    public:
        ~format_guard() { cout.flags(f); }
    };
  3. 定义另一个辅助类scientific_type。因为其是一个模板类,所以其能拥有任意类型的成员变量。这个类没有其他任何作用:

    template <typename T>
    struct scientific_type {
        T value;
    
        explicit scientific_type(T val) : value{val} {}
    };
  4. 封装成scientific_type之后,可以对任意类型进行自定义格式设置,当对operator>>进行重载后,输出流就会在执行时,运行完全不同的代码。这样就能在使用科学计数法表示浮点数时,以大写的格式,并且其为正数时,数字前添加'+'号。我们也会在跳出函数时,使用format_guard类对打印格式进行清理:

    template <typename T>
    ostream& operator<<(ostream &os, const scientific_type<T> &w) {
        format_guard _;
        os << scientific << uppercase << showpos;
        return os << w.value;
    }
  5. 主函数中,我们将使用到format_guard类。我们会创建一段新的代码段,首先对类进行实例化,并且对std::cout进行输出控制符的设置:

    int main()
    {
        {
            format_guard _;
            cout << hex << scientific << showbase << uppercase;
    
            cout << "Numbers with special formatting:\n";
            cout << 0x123abc << '\n';
            cout << 0.123456789 << '\n';
        }
  6. 使用控制符对这些数字进行打印后,跳出这个代码段。这时format_guard的析构函数会将格式进行清理。为了对清理结果进行测试,会再次打印相同的数字。其将会输出不同的结果:

        cout << "Same numbers, but normal formatting again:\n";
        cout << 0x123abc << '\n';
        cout << 0.123456789 << '\n';
  7. 现在使用scientific_type,将三个浮点数打印在同一行。我们将第二个数包装成scientific_type类型。这样其就能按照我们指定的风格进行打印,不过在之前和之后的输出都是以默认的格式进行。与此同时,我们也避免了冗长的格式设置代码:

        cout << "Mixed formatting: "
            << 123.0 << " "
            << scientific_type{123.0} << " "
            << 123.456 << '\n';
    }
  8. 编译并运行程序,我们就会得到如下的输出。前两行按照我们的设定进行打印。接下来的两行则是以默认的方式进行打印。这样就证明了我们的format_guard类工作的很好。最后三个数在一行上,也是和我们的期望一致。只有中间的数字是scientific_type类型的,前后两个都是默认类型:

    $ ./pretty_print_on_the_fly
    Numbers with special formatting:
    0X123ABC
    1.234568E-01
    Same numbers, but normal formatting again:
    1194684
    0.123457
    Mixed formatting: 123 +1.230000E+02 123.456

Last updated