<boost/format.hpp>
format 类以类型安全的方式提供类似 printf 的格式化,并允许输出用户定义的类型。
format 对象从格式字符串构造,然后通过重复调用 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 演示了高级功能的使用,例如重用和修改 format 对象等。
而 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 科学计数法浮点格式 将 floatfield 位设置为 scientific f 定点浮点格式 将 floatfield 位设置为 fixed g 通用 -默认- 浮点格式 取消设置所有 floatfield 位 X、A、E、F 或 G 与其小写对应项效果相同,但使用大写字母进行数字输出。(指数,十六进制数字,..) 与 'x', 'a', 'e', 'f', 或 'g' 分别具有相同的效果,加上 uppercase d、i 或 u 十进制类型输出 将 basefield 位设置为 dec s 或 S 字符串输出 precision 规范未设置,其值进入内部字段以供稍后“截断”。(参见上面的 precision 说明) 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 复制)