5.6. 格式化输出库 Boost.Format

Boost.Format 库可以作为定义在文件 cstdio 中的函数 std::printf() 的替代。 std::printf() 函数最初出现在 C 标准中,提供格式化数据输出功能, 但是它既不是类型安全的有不能扩展。 因此在 C++ 应用中, Boost.Format 库通常是数据格式化输出的上佳之选。

Boost.Format 库在文件 boost/format.hpp 中定义了类 boost::format 。 与函数 std::printf 相似的是,传递给() boost::format 的构造函数的参数也是一个字符串,它由控制格式的特殊字符组成。 实际数据通过操作符 % 相连,在输出中替代特殊字符,如下例所示。

  1. #include <boost/format.hpp>
  2. #include <iostream>
  3.  
  4. int main()
  5. {
  6. std::cout << boost::format("%1%.%2%.%3%") % 16 % 9 % 2008 << std::endl;
  7. }

Boost.Format 类使用置于两个百分号之间的数字作为占位符,占位符稍后通过 % 操作符与实际数据连接。 以上程序使用数字16、9 和 2009 组成一个日期字符串,以 16.9.2008的格式输出。 如果要月份出现在日期之前,即美式表示,只需交换占位符即可。

  1. #include <boost/format.hpp>
  2. #include <iostream>
  3.  
  4. int main()
  5. {
  6. std::cout << boost::format("%2%/%1%/%3%") % 16 % 9 % 2008 << std::endl;
  7. }

现在程序显示的结果变成 9/16/2008

如果要使用C++ 操作器格式化数据,Boost.Format 库提供了函数 boost::io::group()

  1. #include <boost/format.hpp>
  2. #include <iostream>
  3.  
  4. int main()
  5. {
  6. std::cout << boost::format("%1% %2% %1%") % boost::io::group(std::showpos, 99) % 100 << std::endl;
  7. }

本例的结果显示为 +99 100 +99 。 因为操作器 std::showpos() 通过 boost::io::group() 与数字 99 连接,所以只要显示 99 , 在它前面就会自动加上加号。

如果需要加号仅在 99 第一次输出时显示, 则需要改造格式化占位符。

  1. #include <boost/format.hpp>
  2. #include <iostream>
  3.  
  4. int main()
  5. {
  6. std::cout << boost::format("%|1$+| %2% %1%") % 99 % 100 << std::endl;
  7. }

为了将输出格式改为 +99 100 99 ,不但需要将数据的引用符号由 1$ 变为 1% ,还需要在其两侧各添加一个附加的管道符号,即将占位符 %1% 替换为 %|1$+|。

请注意,虽然一般对数据的引用不是必须的,但是所有占位符一定要同时设置为指定货非指定。 以下例子在执行时会出现错误,因为它给第二个和第三个占位符设置了引用但是却忽略了第一个。

  1. #include <boost/format.hpp>
  2. #include <iostream>
  3.  
  4. int main()
  5. {
  6. try
  7. {
  8. std::cout << boost::format("%|+| %2% %1%") % 99 % 100 << std::endl;
  9. }
  10. catch (boost::io::format_error &ex)
  11. {
  12. std::cout << ex.what() << std::endl;
  13. }
  14. }

此程序抛出了类型为 boost::io::format_error 的异常。 严格地说,Boost.Format 库抛出的异常为 boost::io::bad_format_string。 但是由于所有的异常类都继承自 boost::io::format_error 类,捕捉此类型的异常会轻松一些。

以下例子演示了不引用数据的方法。

  1. #include <boost/format.hpp>
  2. #include <iostream>
  3.  
  4. int main()
  5. {
  6. std::cout << boost::format("%|+| %|| %||") % 99 % 100 % 99 << std::endl;
  7. }

第二、第三个占位符的管道符号可以被安全地省略,因为在这种情况下,他们并不指定格式。这样的语法看起来很像 std::printf ()的那种。

  1. #include <boost/format.hpp>
  2. #include <iostream>
  3.  
  4. int main()
  5. {
  6. std::cout << boost::format("%+d %d %d") % 99 % 100 % 99 << std::endl;
  7. }

虽然这看起来就像 std::printf() ,但是 Boost.Format 库有类型安全的优点。 格式化字符串中字母 'd' 的使用并不表示输出数字,而是表示 boost::format 类所使用的内部流对象上的 std::dec() 操作器,它可以使用某些对 std::printf() 函数无意义的格式字符串,如果使用 std::printf() 会导致程序在运行时崩溃。

  1. #include <boost/format.hpp>
  2. #include <iostream>
  3.  
  4. int main()
  5. {
  6. std::cout << boost::format("%+s %s %s") % 99 % 100 % 99 << std::endl;
  7. }

尽管在 std::printf() 函数中,字母 's' 只用于表示类型为 const char* 的字符串,然而以上程序却能正常运行。 因为在 Boost.Format 库中,这并不代表强制为字符串,它会结合适当的操作器,调整内部流的操作模式。 所以即使在这种情况下, 在内部流中加入数字也是没问题的。