<boost/format.hpp>
中的 format 类提供类似 printf 的格式化功能,以类型安全的方式允许输出用户定义的类型。
格式对象由格式字符串构造,然后通过重复调用operator%来传递参数。
每个参数都会被转换为字符串,然后根据格式字符串将这些字符串组合成一个字符串。
cout << boost::format("writing %1%, x=%2% : %3%-th try") % "toto" % 40.23 % 50; // prints "writing toto, x=40.230 : 50-th try"
或稍后(如:cout << format("%2% %1%") % 36 % 77;
你将变量馈送到格式化程序。format fmter("%2% %1%"); fmter % 36; fmter % 77;
// fmter was previously created and fed arguments, it can print the result : cout << fmter ; // You can take the string result : string s = fmter.str(); // possibly several times : s = fmter.str( ); // You can also do all steps at once : cout << boost::format("%2% %1%") % 36 % 77; // using the str free function : string s2 = str( format("%2% %1%") % 36 % 77 );
using namespace std; using boost::format; using boost::io::group;
输出: "11 22 333 22 11 \n"cout << format("%1% %2% %3% %2% %1% \n") % "11" % "22" % "333"; // 'simple' style.
输出: "(x,y) = ( -23, +35) \n"cout << format("(x,y) = (%1$+5d,%2$+5d) \n") % -23 % 35; // Posix-Printf style
输出: "writing toto, x=40.23 : 50-th step \n"cout << format("writing %s, x=%s : %d-th step \n") % "toto" % 40.23 % 50;
所有这些都输出: "(x,y) = ( -23, +35) \n"cout << format("(x,y) = (%+5d,%+5d) \n") % -23 % 35; cout << format("(x,y) = (%|+5|,%|+5|) \n") % -23 % 35; cout << format("(x,y) = (%1$+5d,%2$+5d) \n") % -23 % 35; cout << format("(x,y) = (%|1$+5|,%|2$+5|) \n") % -23 % 35;
两者都输出相同的内容: "_ +101_ 101 \n"format fmter("_%1$+5d_ %1$d \n"); format fmter2("_%1%_ %1% \n"); fmter2.modify_item(1, group(showpos, setw(5)) ); cout << fmter % 101 ; cout << fmter2 % 101 ;
操作符在每次出现 %1% 时都被应用,因此它输出: "_ +101_ +101 \n"cout << format("_%1%_ %1% \n") % group(showpos, setw(5), 101);
对于某些 std::vector names、surnames 和 tel(参见 sample_new_features.cpp),它会输出for(unsigned int i=0; i < names.size(); ++i) cout << format("%1%, %2%, %|40t|%3%\n") % names[i] % surname[i] % tel[i];
Marc-François Michel, Durand, +33 (0) 123 456 789 Jean, de Lattre de Tassigny, +33 (0) 987 654 321
程序 sample_formats.cpp 演示了format 的简单用法。
sample_new_features.cpp 说明了添加到 printf 语法中的几个格式化特性,例如简单的位置指令、居中对齐和'制表符'。
sample_advanced.cpp 演示了高级特性的用法,例如重用和修改格式对象等。
而 sample_userType.cpp 显示了format 库在用户定义类型上的行为。
boost::format( 格式字符串 ) % arg1 % arg2 % ... % argN
格式字符串包含文本,其中特殊指令将被替换为由给定参数的格式化生成的字符串。
C 和 C++ 世界中的传统语法是 printf 使用的语法,因此 format 可以直接使用 printf 格式字符串并产生相同的结果(几乎在所有情况下都是如此。有关详细信息,请参见 与 printf 的不兼容性)。
此核心语法已扩展,以允许新的特性,并适应 C++ 流上下文。因此,format 接受格式字符串中几种形式的指令
Boost.format 支持的 printf 格式说明遵循 Unix98 Open-group printf 的精确语法,而不是标准 C printf(不支持位置参数)。(常用标志在两者中含义相同,因此对任何人来说都不应该是一个难题)
注意,在同一个格式字符串中混合使用位置格式说明(例如%3$+d)和非位置格式说明(例如%+d)是错误的。
在 Open-group 规范中,多次引用同一个参数(例如"%1$d %1$d")具有未定义的行为。Boost.format 在此类情况下的行为是允许多次引用每个参数。唯一的限制是它期望恰好P个参数,P是格式字符串中使用的最大参数编号。(例如,对于 "%1$d %10$d",P == 10)。
提供多于或少于P个参数将引发异常。(除非另有设置,请参见 异常)
说明spec具有以下形式
[ N$ ] [ flags ] [ width ] [ . precision ] [ argument-type ] conversion-specifier方括号内的字段是可选的。以下列表逐一解释了这些字段
标志 含义 对内部流的影响 '-' 左对齐 N/A(稍后应用于字符串) '=' 居中对齐 N/A(稍后应用于字符串)
- 注意:新增功能,printf 中没有 -'_' 内部对齐 设置内部对齐
- 注意:新增功能,printf 中没有 -'+' 即使对于正数也显示符号 设置showpos '#' 显示数字基数和小数点 设置showbase 和 showpoint '0' 用 0 填充(插入在符号或基数指示符之后) 如果不是左对齐,则调用setfill('0')并设置internal
在流转换后执行额外操作以处理 用户定义的输出。' ' 如果字符串不是以+或-开头,则在转换后的字符串前面插入一个空格 N/A(稍后应用于字符串)
与 printf 的行为不同:它不受内部对齐的影响
boost::format
,参数通过operator %
馈送到 format,这允许模板携带参数类型。因此,经典的 printf 风格的参数类型被使用并忽略。参数类型hh
、h
、l
、ll
、j
、z
和L
被识别,Microsoft 扩展w
、I
(大写 i)、I32
和I64
也被识别。ISO C99 标准中的参数类型t
不被识别为参数类型,因为它在boost::format
中多年来一直用作表格输出的转换说明符。
转换说明符 含义 对流的影响 b 布尔字符串输出 设置boolalpha;仅适用于类型 bool
要自定义生成的字符串,请参见 std::numpunct。p 或 x 十六进制输出 设置hex o 八进制输出 设置oct a 十六进制指数表示法 将 floatfield 位设置为scientific | fixed(等效于hexfloat) e 科学浮点格式 将浮点字段位设置为科学计数法 f 定点浮点格式 将浮点字段位设置为定点 g 通用(默认)浮点格式 取消设置所有浮点字段位 X、A、E、F 或 G 与小写字母对应项效果相同,但使用大写字母进行数字输出。(例如指数、十六进制数字……) 与'x'、'a'、'e'、'f' 或 'g' 的效果分别相同,此外大写 d、i 或 u 十进制类型输出 将基数字段位设置为dec s 或 S 字符串输出 精度规范未设置,其值转到内部字段以供以后“截断”。(参见上面精度说明) c 或 C 1 字符输出 仅使用转换字符串的第一个字符。 % 打印字符 % N/A
请注意,忽略了“n”转换说明符(以及相应的参数),因为它不适合此上下文。
printf(s, x1, x2);
cout << format(s) % x1 % x2;
但是,由于某些 printf 格式规范无法很好地转换为流格式选项,因此 Boost.format 模拟 printf 的方式存在一些明显的缺陷。
无论如何,format 类都应静默忽略不受支持的选项,以便 format 始终接受 printf 格式字符串,并产生与 printf 几乎相同的输出。
format formatter("%+5d"); cout << formatter % x; unsigned int n = formatter.size();
所有转换为流状态修改的标志都在用户定义类型中递归地起作用。(标志保持有效,所需的格式选项也如此,对于用户定义类可能调用的每个“<<”操作都是如此)
例如,对于 Rational 类,我们将拥有类似的内容Rational ratio(16,9); cerr << format("%#x \n") % ratio; // -> "0x10/0x9 \n"
对于其他格式选项,情况有所不同。例如,设置宽度适用于对象生成的最终输出,而不是其每个内部输出,这很幸运
cerr << format("%-8d") % ratio; // -> "16/9 " and not "16 /9 " cerr << format("%=8d") % ratio; // -> " 16/9 " and not " 16 / 9 "
但“0”和“ ”选项也是如此(与“+”相反,“+”由showpos直接转换为流状态。但零和空格 printf 选项不存在此类标志)
这不太自然
可以通过仔细设计 Rational 的operator<<来获得更好的行为,它本身处理流的宽度、对齐和showpos参数。这在sample_userType.cpp中进行了演示。cerr << format("%+08d \n") % ratio; // -> "+00016/9" cerr << format("% 08d \n") % ratio; // -> "000 16/9"
format 的内部流状态在参数输出之前保存,并在之后恢复;因此,修饰符不是粘性的,只影响其应用到的参数。
根据标准,流的默认状态为:精度 6,宽度 0,右对齐,并设置十进制标志。
可以通过与参数一起传递的操纵器来更改内部format流的状态;通过group函数,如下所示
cout << format("%1% %2% %1%\n") % group(hex, showbase, 40) % 50; // prints "0x28 50 0x28\n"
在“group”内部传递 N 个项目时,Boost.format 需要以与常规参数不同的方式处理操纵器,因此使用 group 受以下约束
此类操纵器在每次出现时都会在下一个参数之前传递到流。请注意,格式字符串中指定的格式选项会被以此方式传递的流状态修饰符覆盖。例如,在以下代码中,hex操纵器优先于格式字符串中的d类型规范,该规范将设置十进制输出
cout << format("%1$d %2% %1%\n") % group(hex, showbase, 40) % 50; // prints "0x28 50 0x28\n"
Boost.format 对 format 对象的使用强制执行许多规则。格式字符串必须符合上面描述的语法,用户必须在输出到最终目标之前提供完全正确的参数数量,如果使用 modify_item 或 bind_arg,则项目和参数索引不得超出范围。
当 format 检测到不满足这些规则之一时,它会引发相应的异常,以使错误不会被忽视和未处理。
但是用户可以更改此行为以适应其需求,并使用以下函数选择哪些类型的错误可能会引发异常
unsigned char exceptions(unsigned char newexcept); // query and set unsigned char exceptions() const; // just query
用户可以使用二进制算术组合以下原子来计算参数newexcept
例如,如果您不希望 Boost.format 检测参数数量错误,您可以定义一个用于构建具有正确异常设置的 format 对象的特定包装器函数
然后允许提供比需要更多的参数(它们只是被忽略)boost::format my_fmt(const std::string & f_string) { using namespace boost::io; format fmter(f_string); fmter.exceptions( all_error_bits ^ ( too_many_args_bit | too_few_args_bit ) ); return fmter; }
如果我们在提供所有参数之前请求结果,则结果的相应部分将为空cout << my_fmt(" %1% %2% \n") % 1 % 2 % 3 % 4 % 5;
cout << my_fmt(" _%2%_ _%1%_ \n") % 1 ; // prints " __ _1_ \n"
可以将 boost::format 用于对少量内置类型参数进行重新排序的格式化的性能与 Posix-printf 的性能以及等效的流手动操作的性能进行比较,以衡量由此产生的开销。结果可能很大程度上取决于编译器、标准库实现以及格式字符串和参数的精确选择。
常见的流实现最终会调用 printf 系列的函数来实际格式化数字。通常,由于重新排序开销(分配存储字符串片段,每个项目格式化时的流初始化……),printf 将明显快于直接流操作。直接流操作将比 boost::format 快——可以预期比率在 2 到 5 或更高之间。
当迭代格式化是性能瓶颈时,可以通过将格式字符串解析为 format 对象,并在每次格式化时复制它,来略微提高性能,方法如下所示
const boost::format fmter(fstring); dest << boost::format(fmter) % arg1 % arg2 % arg3 ;
作为性能结果的一个示例,作者测量了使用 4 种不同方法进行迭代格式化的执行时间
该测试使用 g++-3.3.3 编译,并测量了以下时间(以秒和比率表示)
string fstring="%3$0#6x %1$20.10E %2$g %3$0+5d \n"; double arg1=45.23; double arg2=12.34; int arg3=23; - release mode : printf : 2.13 nullStream : 3.43, = 1.61033 * printf boost::format copied : 6.77, = 3.1784 * printf , = 1.97376 * nullStream boost::format straight :10.67, = 5.00939 * printf , = 3.11079 * nullStream - debug mode : printf : 2.12 nullStream : 3.69, = 1.74057 * printf boost::format copied :10.02, = 4.72642 * printf , = 2.71545 * nullStream boost::format straight :17.03, = 8.03302 * printf , = 4.61518 * nullStream
namespace boost { template<class charT, class Traits=std::char_traits<charT> > class basic_format { public: typedef std::basic_string<charT, Traits> string_type; typedef typename string_type::size_type size_type; basic_format(const charT* str); basic_format(const charT* str, const std::locale & loc); basic_format(const string_type& s); basic_format(const string_type& s, const std::locale & loc); basic_format& operator= (const basic_format& x); void clear(); // reset buffers basic_format& parse(const string_type&); // clears and parse a new format string string_type str() const; size_type size() const; // pass arguments through those operators : template<class T> basic_format& operator%(T& x); template<class T> basic_format& operator%(const T& x); // dump buffers to ostream : friend std::basic_ostream<charT, Traits>& operator<< <> ( std::basic_ostream<charT, Traits>& , basic_format& ); // Choosing which errors will throw exceptions : unsigned char exceptions() const; unsigned char exceptions(unsigned char newexcept); // ............ this is just an extract ....... }; // basic_format typedef basic_format<char > format; typedef basic_format<wchar_t > wformat; // free function for ease of use : template<class charT, class Traits> std::basic_string<charT,Traits> str(const basic_format<charT,Traits>& f) { return f.str(); } } // namespace boost
此类的目标是带来更好的 C++、类型安全且可扩展类型的printf等效项,可与流一起使用。
准确地说,format旨在提供以下功能在设计过程中,遇到了许多问题,并做出了一些选择,这些选择可能并不直观正确。但在每种情况下,它们都是出于某些原因而做出的。
Boost format 的作者是 Samuel Krempp。他使用了 Rüdiger Loos 的 format.hpp 和 Karl Nelson 的格式化类的想法。
修订版2017 年 10 月 23 日
版权所有 © 2002 Samuel Krempp
本软件在 Boost 软件许可证 1.0 版下发行。(许可证文本位于附带文件 LICENSE_1_0.txt 中,您也可以从 https://boost.ac.cn/LICENSE_1_0.txt 获取)