文档章节

Boost 格式化输出函式库Format

吃一堑消化不良
 吃一堑消化不良
发布于 2015/09/13 12:46
字数 2823
阅读 289
收藏 2
点赞 0
评论 0

他最大的特色是在于它可以使用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上应该是可以用的。 

© 著作权归作者所有

共有 人打赏支持
吃一堑消化不良
粉丝 28
博文 187
码字总数 112458
作品 0
浦东
程序员
boost format库怎么取消 数字千分位的逗号分隔符?

小弟在用boost::format库时希望格式化数字后的字符串中不带千分位分隔符(通常是逗号); 可以设置么?

docici
2013/12/11
592
1
C++ boost 正则表达式用法

什么是正则表达式?正则表达式是一种用来描述一定数量文本的模式。Regex代表Regular Express。 如果您不知道什么是正则表达式,请看这篇文章http://blog.csdn.net/begtostudy/archive/2007/...

zungyiu
2010/12/25
0
0
(一)boost库之日期、时间

一、计时器 计时器,通常在一个项目中统计一个函数的执行时间是非常实用的。 #include <boost/timer.hpp> void PrintUserTime() { boost::timer t; //定义一个计时类,开始计时 std::cout <<...

fanzhao
2014/03/22
0
1
java格式化数字DecimalFormat

在输出数字时,有时需要给数字配上单位,有时需要数字具有一定的精度,也有时需要用科学计数法表示数字。 关键技术剖析: v java.text.DecimalFormat类专门用于格式化数字。 v 需要为Decimal...

wf王帆
2016/05/25
70
0
go-logger 日志工具包 v1.2 发布

go-logger 一个简单而强大的 golang 日志工具包 功能 支持同时输出到 console, file, url 命令行输出字体可带颜色 文件输出支持根据 文件大小,文件行数,日期三种方式切分 文件输出支持根据...

phachon
04/23
0
0
boost-log-库 使用经历

最近想整一个日志库,以前使用过log4cplus,看到了boost推出了1.54版本之后,就开始有了一个log库。这个log看了介绍,之后开始尝试去使用。主要的应用场景是让多个模块(so,dll)使用。场景是一...

wclhjs
2014/08/05
0
3
内置组件和

第一部分:源码 Output.java // Copyright 2007, 2008 The Apache Software Foundation//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file......

阎王他爹
2011/04/03
0
0
Java中的格式化输出

说在前面:本文大部分内容和代码来自《Thinking in Java》。这本书真的是一本好书,强烈推荐。 在Java SE5(很久很久以前的版本)引入了format方法,这个方法跟C的printf方法很像。首先用个简...

英强
2016/04/23
198
0
Delphi 中Format的字符串格式化使用说明

一、Format函数的用法 Format是一个很常用,却又似乎很烦的方法,本人试图对这个方法的帮助进行一些翻译,让它有一个完整的概貌,以供大家查询之用: 首先看它的声明: function Format(cons...

老朱教授
2017/08/27
0
0
简单扩展性好的日志库 - Go Logger

go-logger 一个简单而强大的 golang 日志工具包 English document 功能 支持同时输出到 console, file, url 命令行输出字体可带颜色 文件输出支持根据 文件大小,文件行数,日期三种方式切分...

phachon
2017/10/12
0
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

垃圾回收算法

一 如何判断对象可以回收 1 引用计数法 思路大概为:给对象添加一个引用计数器,每当有一个地方引用它时,计数器值加1;当引用失效时,计数器减1;任何时刻计算器为0的对象就是不可能再被使用...

sen_ye
3分钟前
0
0
Activiti简介(学习总结一)

一、介绍 activiti是使用命令模式设计基于bpmn2.0的一款开源工作流引擎。 工作流简单举例:提交请假申请->经理审批->结束。这就是一个简单流程。activiti支持用户自定义流程。配置各个流程对...

沙shasha
3分钟前
0
0
VCL界面控件DevExpress VCL Controls发布v18.1.3|附下载

DevExpress VCL Controls是 Devexpress公司旗下最老牌的用户界面套包。所包含的控件有:数据录入,图表,数据分析,导航,布局,网格,日程管理,样式,打印和工作流等,让您快速开发出完美、...

Miss_Hello_World
4分钟前
0
0
加米谷大数据培训:云计算、大数据和人工智能之间的关系

一般谈云计算的时候会提到大数据、谈人工智能的时候会提大数据、谈人工智能的时候会提云计算……感觉三者之间相辅相成又不可分割。 一、云计算最初的目标 云计算最初的目标是对资源的管理,管...

加米谷大数据
9分钟前
1
0
java集合元素的默认大小

当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全部复制到新的内存上,这无疑使...

竹叶青出于蓝
12分钟前
1
0
Java快速开发平台,JEECG 3.7.7闪电版本发布,增加多套主流UI代码生成器模板

JEECG 3.7.7 闪电版本发布,提供5套主流UI代码生成器模板 导读 ⊙平台性能优化,速度闪电般提升 ⊙提供5套新的主流UI代码生成器模板(Bootstrap表单+BootstrapTable列表\ ElementUI列表表单)...

Jeecg
15分钟前
0
0
export 和 module.export 的区别

在浏览器端 js 里面,为了解决各模块变量冲突等问题,往往借助于 js 的闭包把左右模块相关的代码都包装在一个匿名函数里。而 Nodejs 编写模块相当的自由,开发者只需要关注 require,exports,...

孟飞阳
17分钟前
1
0
技术教育的兴起

技术教育的兴起 作者: 阮一峰 1、 有一年,我在台湾环岛旅行。 花莲的海边,我遇到一对台湾青年夫妻,带着女儿在海滩上玩。我们聊了起来。 当时,我还在高校当老师。他们问我,是否觉得台湾...

吕伯文
17分钟前
0
0
Linux服务器下的HTTP抓包分析

说到抓包分析,最简单的办法莫过于在客户端直接安装一个Wireshark或者Fiddler了,但是有时候由于客户端开发人员(可能是第三方)知识欠缺或者其它一些原因,无法顺利的在客户端进行抓包分析,...

mylxsw
22分钟前
0
0
mybatis3-javaapi

sqlSessionFactoryBuilder->sqlSessionFactory->sqlSession<-rowbound<-resultHandler myBatis uses a Java enumeration wrapper for transaction isolation levels, called TransactionIsol......

writeademo
25分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部