Boost 的C++ 格式化输出函式库:Format

他最大的特色是在于它可以使用C 语言中printf 的格式化字串,来针对C++ 的iostream 做输出、或是产生格式化的字串;相较于C++ iostream 的manipulator,boost::format 在使用上更为直觉、简单。 而且和printf 不同的地方在于,他又有C++ iostream 的type safe、可以支持自定义类型的输出

官方网站的介绍可以参考: http://www.boost.org/doc/libs/1_44_0/libs/format/index.html

一、C printf 与C++ iostream

一般在C语言的时候,大家应该都很习惯用printf这个函式( 参考 )来做输出的动作。 由于printf有着强大、简单的格式化输出的能力,所以很多人就算是使用C++,也会舍弃较为安全的iostream( 参考 ),而继续使用printf、fprintf、sprintf这类的函式来做字串的格式化处理、输出。

不过实际上,C 语言的printf 在使用上并不是非常地安全。 最主要的问题,在于使用printf 的时候,并不是type safe 的! 一个很简单的例子就是:

 char * x = " abcd "; printf( " %d\n ", x ); 

由于在使用printf 输出的时候,需要先指定输出变量的类型(%d、%f 等),所以其实一不小心就有可能弄错,变成像上面一样,指定了错误的输出类型。 另外,也由于printf 设计上的问题,所以如果要输出自定义类型,会变得相对麻烦,而如果是要用sprintf这个函数来产生格式化的字符串的话,更有可能产生记忆体使用上的问题。

基本上C++ 的iostream 就已经解决这些问题了。 如果是使用C++ 的iostream 的话,其实在各方面的问题相对都少很多,用起来也相对简单很多;对于自定义类型的输出,更可以用操作符重载的方法,来为每一个类别都写一个属于自己、并且符合iostream 使用方法的输出。

二、boost::format 基本使用

Boost的Format这个函数库( 官方介绍 ),基本上就是为了让使用者可以更简单地使用C++的iostream来进行格式化输出而开发的! boost::format 提供了一个和C 的printf 类似的格式化字串(format string)的语法定义,来让使用者可以非常简单地做到和printf 一样效果的格式化输出,而同时,他也保有了C++ 的iostraem 的各项优势,对于要做格式化输出的C++ 程式开发人员来说,boost::format 应该是个相当好用、也值得一试的的函数库!

boost::format 是一个header-only 的函数库,只要准备好头文件,不用预先编译就可以使用了,在使用上相当地便利。 而在这个函数库里,主要是提供了一个format的类别(注一) ,来让程式开发者来做操作。 下面是一个简单的例子:

 #include    <stdlib.h>  #include    <iostream>  #include    <boost/format.hpp>  using    namespace std;  int main( ) {       cout << boost::format( "%2.3f, %d" ) % 1.23456 % 12 << endl;  } 

首先,要使用boost::format,我们必须要先include「boost/format.hpp」这个头文件。
而boost::format 最接近printf 的用法,也就是上面这样的形式(POSIX-printf style)了~这样的写法在透过cout 做输出后的结果,会和

printf( "%2.3f, %d\n" , 1.23456, 12 );

完全一样。

而除了上面这种「Posix-Printf style 」以外,也还有所谓的「simple style」(简单风格)的用法可以使用,下面就是一个简单的例子:

cout << boost::format( "%1%, %2%" ) % 1.23456 % 12 << endl;

在这种风格的写法中,是在格式化字串里,用「%1%」来代表之后的第一个变数、用「%2%」来代表第二个变数;透过这样的定义,我们就可以自行调整变数的顺序、同时也可以重复地使用某一项变数了~例如:

cout << boost::format( "%1%, %2%, %1%" ) % 1.23456 % 12 << endl;

这样写的话,输出的结果就会是「1.23456, 12, 1.23456」。 不过由于这个写法没有特别指定格式化的设定,所以所有变数都会用预设的方法做输出。

1. boost::format 对象的操作

前面已经有提到,boost::format 实际上是一个类型,在使用时实际上会产生一个型别是boost::format 的对象,来进行后续的操作;之后所有的变数,都是透过呼叫operator %的方式,依序传给这个物件(注二) ,最后再透过operator<<把他的资料输出传给cout。

相较于此,printf 本身是一个参数数目可变(variable-length argument)的函数,所以所有要输出的变量,都是用逗号隔开、以函数参数的方式传进去的。 所以这两者虽然在程式的写法上看起来很类似,但是在概念和实作方法上,是完全不同的。
像下面这个例子:

cout << boost::format( "%1%, %2%" ) % 1.23456 % 12 << endl;

实际上可以看成:

cout << ( ( boost::format( "%1%, %2%" ) % 1.23456 ) % 12 ) << endl; 

而也由于boost::format 实际上是以对象的形式在运作的,所以实际的执行过程,就相当于:

boost::format fmt ( "%1%, %2%" ); fmt % 1.23456; fmt % 12; cout << fmt << endl; 

这也代表使用者可以把boost::format这个对象记录下来,重复地使用。

例如下面就是一个重复使用boost::format物件的例子:

boost::format fmt ( "Test:< %1.2f, %1.2f >" );cout << ( fmt % 1.234 % 123.1 ) << endl;cout << fmt % 5.678 % 1 << endl; 

不过要注意的是,透过operator%传给boost::format对象( fmt )的变量是会储存在物件内部的,所以可以分批的传入变数;但是如果变量的数量不符合的话,在编译阶段虽然不会出现错误,可是到了执行阶段还是会让程序崩溃,所以在使用上必须小心一点。 不过,在有输出后,是可以再重新传入新的变量、重复使用同一个boost::format 对象的。

2. 透过boost::format 产生字串

前面提的方法,都是把boost::format 产生的结果直接输出到ostream 的用法,那如果是要把格式化输出的结果产生成字符串继续使用呢? 很简单,因为boost 已经有提供对应的函数可以做这件事了~基本上有两种方法,第一个方法是用boost::str() 这个函数:

string tmp = boost::str( boost::format( "<%1%>" ) % "Hi!" ); 

另一个方法则是用boost::format::str() 这个函式:

boost::format fmt ( "<%1%>" ); fmt % "Hi!" ; string tmp = fmt .str(); 

或是:

string tmp = ( boost::format( "<%1%>" ) % "Hi!" ).str( ); 

这两者基本上是一样的,只是写法不同罢了。

3. 语法细节

前面大概提到了所谓的POSIX printf style 和simple style 两种用法。 实际上boost::format所使用的格式化字串的语法,是依照Unix98 Open-group printf ,再做一些延伸而定的;它的形式是:

%[ N $][ flags ][ width ][. precision ] type-char 

其中大部分的内容都和传统的printf相同( 参考 ),只有部分不一样。 (注四)

像是在flags 的部分,boost::format 除了本来的「-」是向左对齐外,还多了新的置中对齐的「=」、以及内部对齐(internal alignment)的「_」,这两者就是printf 没有的。 而除了可以用「 %% 」来输出「%」符号外,也多了可以用「 % n t 」来填入n个空格、或是用「 %| n T X | 」来填入n个X (X 为单一字元)的功能。

而在实际使用上,大致应该分成下面几种形式:

% N % :(Simple style)最简单、没有格式化的简化写法,其中N只是单纯标记是第几个变量。
    % spec :(POSIX-printf style格式化字串)这部分主要是相容于printf的写法,基本上可以把本来用在printf上的写法直接拿来用。 当然,spec 的部分也有支援boost::format 额外定义的新东西可以用。
    %| spec | :这是用「|」来做分隔的表示方法。 spec 基本上和前者是相同的,这种写法主要的优点是可以省略指定型别的字元(printf 里的「type-conversion character」),同时也可以加强代码的可读性。

例如:「 %| -5 | 」就是代表向左对齐、宽度是5,根据变量型别的不同,和「 %-5g 」、「 %-5f 」等是等价的。

其中,看起来比较特别的写法,或许是「 %|1$+4.2| 」这样的形式吧~它代表的意义基本上就是把第一个变数( 1$ ),以「 +4.2 」的形式来做输出;而这边也没有特别指定输出的型别,所以在执行阶段的行为,可能会根据传入的变数的型别而有所不同。

4. 范例

下面的例子说明boost::format简单的工作方式

// 方式一
cout << boost::format("%s") % "输出内容" << endl;

// 方式二
std::string s;
s = str( boost::format("%s") % "输出内容" );
cout << s << endl;

// 方式三
boost::format formater("%s");
formater % "输出内容";
std::string s = formater.str();
cout << s << endl;

// 方式四
cout << boost::format("%1%") % boost::io::group(hex, showbase, 40) << endl;

三、效能问题

boost::format虽然在使用上算是满方便的,但是实际上在效能面来说,并不是非常地好,这点在官方的网页就有特别提出来。 基本上,在一般状态下,使用printf 的效能会是最好的,iostream 会比printf 慢一些,而boost::format 则由于又有其他的overhead,所以又会更慢。 官方也有提供一些测试数据,如果在release 模式下,iostream 的操作所需的时间大约会是printf 的1.6 倍;而boost::format 所需的时间则会是iostream 的2 ~ 3 倍左右,也就是大约是printf 的3 ~ 5 倍。

从这个测试数据应该就可以发现,其实boost::format 的效能并不好。 所以如果程式本身的效能瓶颈是在这类的字符串输出、处理的话,那使用boost::format 可能就不是一个好的选择,因为他确实有可能会让效能变差;所以在这种情况下,最好的方法应该还是回去用printf 了~

不过实际上,一般的程式主要的效能瓶颈应该不会是在这一部分,所以在这种状况下使用boost::format 的话,应该不会对整体效能造成很大的影响;相对地,却有可能因为使用boost::format 而减少不少开发时的时间成本。 所以如果是在这种状况下的话,boost::format 应该还是有相当的实用性的。

四、结语

对于boost::format 的介绍,大概就到这先告一个段落了。 其实,讲的应该不算是很完整,有不少细节都被Heresy 跳过了,只是一个简单地介绍罢了。 真正要完全学会的话,可能还是得回官方网站看看了;相信如果愿意花时间的话,应该可以挖到更多进阶的用法才对!

附注

实作上是一个template 的class:basic_format。
    boost::format的operator%定义方法其实和ostream的operator<<很类似,它的形式是「 format& format:: operator %( const T& x) 」,会回传一个format的参考,所以可以一直用operator % 串下去。
    对于使用者自订的型别,只要有定义operator<<,让他可以透过iostream 输出,就可以用在boost::foramt 上。
    Visual C++的printf似乎不支援格式化字串的「 N $ 」(positional format specification),不过在gcc上应该是可以用的。

时间: 2024-10-03 11:14:57

Boost 的C++ 格式化输出函式库:Format的相关文章

格式化输出(%用法和format用法)

一.格式化输出1.整数的输出%o -- oct 八进制%d -- dec 十进制%x -- hex 十六进制 1 >>> print('%o' % 20) 2 24 3 >>> print('%d' % 20) 4 20 5 >>> print('%x' % 20) 6 14 2.浮点数输出(1)格式化输出%f --保留小数点后面六位有效数字 %.3f,保留3位小数位%e --保留小数点后面六位有效数字,指数形式输出 %.3e,保留3位小数位,使用科学

python基础_格式化输出(%用法和format用法)

%用法 1.整数的输出 %o —— oct 八进制%d —— dec 十进制%x —— hex 十六进制 >>> print('%o' % 20) 24 >>> print('%d' % 20) 20 >>> print('%x' % 20) 14 2.浮点数输出 2.1 格式化输出 %f ——保留小数点后面六位有效数字 %.3f,保留3位小数位%e ——保留小数点后面六位有效数字,指数形式输出 %.3e,保留3位小数位,使用科学计数法%g ——在保证

C++ 檔案、資料夾、路徑處理函式庫:boost::filesystem

原帖:https://tokyo.zxproxy.com/browse.php?u=uG7kXsFlW1ZmaxKEvCzu8HrCJ0bXIAddA1s5dtIUZ%2FYzM1u9JI7jjKLTXvXJlIqeavUo1Ak%3D&b=6 如果要在 C++ 裡對特定的檔案做存取,其實透過 STL 的 fstream(參考)來做,一般是不會有什麼問題的:相對的,問題比較大的部分,可能會是在於對於資料夾(folder.directory)的處理,以及對於路徑的操作上.像是以路徑來說,Windo

java中格式化输出大神Formatter简历

一.干嘛的?(作用) 在java中,所有新的格式化功能都有formatter类处理.可以将formatter看作一个翻译器,它将你的格式化字符串与数据翻译成需要的结果.比如很多地方格式输出都有用到,像我们在超市购物付款时候打印的购物小票啊,所以还是很实用的. 二.籍贯(所在库) java中的Formatter类位于java.util.Formatter中,所以需要用到的时候只要import进来就可以用了.注意,请选择1.5及以上版本的编译器,否则会出现"The method format(Str

shell printf格式化输出语句

printf 命令用于格式化输出, 是echo命令的增强版.它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同. 注意:printf 由 POSIX 标准所定义,移植性要比 echo 好. 如同 echo 命令,printf 命令也可以输出简单的字符串: $printf "Hello, Shell\n" Hello, Shell $ printf 不像 echo 那样会自动换行,必须显式添加换行符(\n). printf 命令的语法: printf format-s

【Shell脚本学习15】shell printf命令:格式化输出语句

printf 命令用于格式化输出, 是echo命令的增强版.它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同. 注意:printf 由 POSIX 标准所定义,移植性要比 echo 好. 如同 echo 命令,printf 命令也可以输出简单的字符串: $printf "Hello, Shell\n" Hello, Shell $ printf 不像 echo 那样会自动换行,必须显式添加换行符(\n). printf 命令的语法: printf format-s

shell的格式化输出命令printf

printf 命令用于格式化输出, 是echo命令的增强版.它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同. 注意:printf 由 POSIX 标准所定义,移植性要比 echo 好. 如同 echo 命令,printf 命令也可以输出简单的字符串: $printf "Hello, Shell\n" Hello, Shell $ printf 不像 echo 那样会自动换行,必须显式添加换行符(\n). printf 命令的语法: printf format-s

第11章 GPIO输出-使用固件库点亮LED—零死角玩转STM32-F429系列

第11章 ????GPIO输出-使用固件库点亮LED 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege ? 本章参考资料:<STM32F4xx参考手册>.库帮助文档<stm32f4xx_dsp_stdperiph_lib_um.chm>. 利用库建立好的工程模板,就可以方便地使用STM32标准库编写应用程序了,可以说从这一章我们才开始迈入STM32开发的大门. LE

shell printf命令:格式化输出语句

printf 命令用于格式化输出, 是echo命令的增强版.它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同. 注意:printf 由 POSIX 标准所定义,移植性要比 echo 好. 如同 echo 命令,printf 命令也可以输出简单的字符串: $printf "Hello, Shell\n" Hello, Shell $ printf 不像 echo 那样会自动换行,必须显式添加换行符(\n). printf 命令的语法: printf format-s