> For the complete documentation index, see [llms.txt](https://zoupers.gitbook.io/cpp-17-stl-cookbook/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://zoupers.gitbook.io/cpp-17-stl-cookbook/chapter7-0-chinese/chapter7-13-chinese.md).

# 简单打印不同格式的数字

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

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

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

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

## How to do it...

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

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

   ```cpp
   #include <iostream>
   #include <iomanip>

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

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

   ```cpp
   template <typename T>
   struct scientific_type {
       T value;

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

   ```cpp
   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`进行输出控制符的设置：

   ```cpp
   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`的析构函数会将格式进行清理。为了对清理结果进行测试，会再次打印相同的数字。其将会输出不同的结果：

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

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

   ```cpp
   $ ./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
   ```
