简单打印不同格式的数字

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

  • 输入输出控制符是有粘性的,所以当我们要临时使用的时候,需要在用完之后进行还原。
  • 其控制符和较少的要打印的对象相比,会显得很冗长。

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

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

How to do it…

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

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

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

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

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

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

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

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

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

    1. $ ./pretty_print_on_the_fly
    2. Numbers with special formatting:
    3. 0X123ABC
    4. 1.234568E-01
    5. Same numbers, but normal formatting again:
    6. 1194684
    7. 0.123457
    8. Mixed formatting: 123 +1.230000E+02 123.456