boost翻译(2):Boost Format Library

Boost Format Library
<boost/format.hpp>中的format类提供一种类似于printf的格式化,这种格式化是类型安全的,并且允许输出用户自定义类型。
    * Synopsis
    * How it works
    * Examples
    * Syntax
          o printf format-specification syntax
          o Incompatibilities with printf
    * Manipulators and the internal stream state
    * User-defined types
    * Alternatives
    * Exceptions
    * Performance
    * Class Interface Extract
    * Rationale

大纲

    一个format对象从一个format-string中被构造出来, 然后通过反复的调用操作符%来输入参数。随后这些参数都会被转换为字符串,并最终根据format-string合并为一个字符串。
    cout  <<  boost::format( " writing %1%,  x=%2% : %3%-th try " %   " toto "   %   40.23   %   50 ;
         
//  输出 "writing toto,  x=40.230 : 50-th try"


它是怎样工作的
    1.当你调用format(s)时,(s是指format-string),它创建一个对象,这个对象会解析format-string,查找出format-string里面所有指示符,并为下一步准备内部结构。
    2.然后,或者立即将变量代入格式程序中,如
 
         cout  <<  format( " %2% %1% " %   36   %   77  );

      或者在稍后将变量代入格式程序中,如   
          format fmter( " %2% %1% " );
          fmter 
%   36 ; fmter  %   77 ;

    这些变量会被抛入到内部流中,如果format-string中给出了格式化选项,那么内部流根据选项来设置状态,format对象会存储最终的字符串结果。   
    3.一旦所有参数都被代入,你就可以将format对象传给一个流,或者通过成员函数str()或boost名字空间中的自由函数str(const format&)得到字符串。作为结果的字符串将会一直保留在format对象中,直到其他参数被传入,此时对象将会被重新初始化。
            //  fmter 已经被创建并传入参数,它可以输出结果 :
          cout  <<  fmter ; 

          
//  你可以从中取出作为结果的字符串 :
           string  s   =  fmter.str();

          
//  可以获取多次 :
          s  =  fmter.str( );

          
//  你也可以一次性执行所有操作 :
          cout  <<  boost::format( " %2% %1% " %   36   %   77 ;

          
//  使用只有函数str:
           string  s2  =  str( format( " %2% %1% " %   36   %   77  );


   4. 可以考虑,进行第3步以后,你可以重复使用一个format对象,并接着从第二步开发 : fmter % 18 % 39;
      为了能够格式化新变量,保存在第一步中执行的代价不菲的操作。

    总而言之, format类将一个format-string解释成对内部流的多个操作,并最终以字符串的格式返回格式化结果,或直接输入到输出流中。


例子:

     using   namespace  std;
    
using  boost::format;
    
using  boost::io::group;


    * 通过重新排序进行简单的输出 :

          cout  <<  format( " %1% %2% %3% %2% %1%  " %   " 11 "   %   " 22 "   %   " 333 " // 简单方式.


      它输出 : "11 22 333 22 11 /n"
    * 通过后缀指令,得到更精确的格式 :

 
         cout  <<  format( " (x,y) = (%1$+5d,%2$+5d)  " %   - 23   %   35 ;    

      它输出 : "(x,y) = ( -23, +35) /n"
    *经典的输出指令,不重新排序 :

          cout  <<  format( " writing %s,  x=%s : %d-th step  " %   " toto "   %   40.23   %   50 ;

     它输出 : "writing toto, x=40.23 : 50-th step /n"
    * 表达同一种输出的多种方式 :

          cout  <<  format( " (x,y) = (%+5d,%+5d)  " %   - 23   %   35 ;
          cout 
<<  format( " (x,y) = (%|+5|,%|+5|)  " %   - 23   %   35 ;

          cout 
<<  format( " (x,y) = (%1$+5d,%2$+5d)  " %   - 23   %   35 ;
          cout 
<<  format( " (x,y) = (%|1$+5|,%|2$+5|)  " %   - 23   %   35 ;

     所有语句都输出: "(x,y) = ( -23, +35) /n"
    * 通过操纵函数修改format-string :

 
         format fmter( " _%1$+5d_ %1$d  " );

          format fmter2(
" _%1%_ %1%  " );
          fmter2.modify_item(
1 , group(showpos, setw( 5 )) );

          cout 
<<  fmter  %   101  ;
          cout 
<<  fmter2  %   101  ;


     输出是一样的 : "_ +101_ 101 /n"
    * 使用带有参数的操纵函数 :

 
         cout  <<  format( " _%1%_ %1%  " %  group(showpos, setw( 5 ),  101 );


      操纵函数会被应用到所有包含%1%的地方,因此会输出 : "_ +101_ +101 /n"
    *新格式化特色:‘absolute tablations',使用内部循环来保证一个字段在多行中都是被打印在同一个位置,即使前一个参数的宽度变化很大。
       for (unsigned  int  i = 0 ; i  <  names.size();  ++ i)
          cout 
<<  format( " %1%, %2%, %|40t|%3% " %  names[i]  %  surname[i]  %  tel[i];



      names, surnames和tel使用某些std::vector时  它输出:

          Marc-Fran?ois Michel, Durand,           +33 (0) 123 456 789
          Jean, de Lattre de Tassigny,                    +33 (0) 987 654 321


语法

    boost::format( format-string ) % arg1 % arg2 % ... % argN

    format-string包含文本,文本中的指示符将会被由给出的参数转换而来的字符串替代。
    合法的语法是C和C++中被printf使用的语法,因此,fromat可以直接使用printf的format-string,并产生同样的结果
    核心的语法被拓展,从而可以产生一些新特色,并与C++的streams搭配使用。因此。format在format-string中可以接受多种指示符。
    *合法的printf格式化字符串:%spec,spec是一个printf格式化说明符。
      spec传递格式化选项,例如宽度,位置,格式化数据时的数据类型说明。但是printf中的经典类型说明符在format中不够充分。它仅仅在内部stream中设置合适的标志以及格式化参数,却不能表明将对应的参数限定为一个特定的类型。
    e.g:格式2$x,在printf中是指”用十六进制将整数参数2打印“,在format中仅仅是指“将stream的标志设置为16进制,并将参数2输出”。
    *%|spec|,spec是一个printf格式化说明符
    括号被用来改善format-string的可读性,同时也使得spec中的类型转换字符成为多选的。这个信息对于C++变量是不需要的,但是对于直接的printf语法,由于它通常提供了一种类型转换字符,判断出格式说明符的结尾,因此这些信息是很必要的。
    e.g. : "%|-5|" 将下一个参数的宽度设置为5, 左对齐方式,就像下列printf指示符 : "%-5g", "%-5f", "%-5s" ..
    * %N%
     这个简单的定位符需要第n个参数来进行格式化,不需要任何格式化选项。
    (它只是printf定位指令(如“%N$s“)的一种简写,主要优点是可读性强,不需要使用类型转换符。)
     新特性在标准的printf格式说明符基础上进行实现,如居中。可以参考新格式的说明。

-printf 格式说明符

    boost.format严格依照Unix98 Open-group printf 语法来支持printf格式说明符, 不依照标准C的printf语法是由于它不支持定位参数。(对于普通标记来说,它们的意义是一样的,所以这不会成为一件令人头痛的事情)
注意,在同一个format string中混合使用定位格式符(如%3$+d)和非定位的格式符(如%+d)将会产生一个错误。
在Open-group说明中,多次重复使用一个参数(如%1$d%1$d)将会导致不确定行为,boost.format却支持这种使用方式。唯一的限制是如果在format string中出现的最大数字是P,那么就需要P个参数。 (如在 "%1$d %10$d",中 P == 10 ).参数的个数过多或多少都会出现一个异常。

一个说明符的格式为 : [ N$ ] [ flags ] [ width ] [ . precision ] type-char

在方括号中的字段是可选的。各字段的意义如下 :

    * N $ 说明格式说明符将应用与第N个参数。(它被称为定位说明符)
      如果没有出现定位说明符,那么参数就会按顺序一一对应。(在后面提供定位说明符将会出现错误)
    * flags 是下列符号的任意一种排列 :

          Flag             意义              对内部流的作用
          '-'              左对齐         N/A ((随后应用于字符串上)
          '='              中间对齐     N/A (随后应用于字符串上)
          - note :     新特性
          '_'             内部对齐
          - note :     新特性
          '+'             显示符号         正号也显示
          '#'         显示基数和小数点 显示基数和小数点
          '0'         0填充
          ' '      如果字符串不含正负号,
                 用空格填充符号          N/A (随后应用于字符串上)
         
    * width 指定转换后的字符串最小宽度。如果必要的话, 字符串会被复制填充,或格式字符(如 flags '0', '-', ..)
      注意,宽度不只是应用于转换流中。为了支持用户自定义类型的输出,宽度是在对整个参数对象进行转换后进行处理的。
 
    * precision (在点号后面), 设置流的精度
          o 当输出浮点数时, 它设置数字的位数
                + 科学计数法时,指小数点后面的位数
                + 普通模式下,指所有数字的位数
          o 与字符有关类型一起使用时, 它指的是另外一个意义 : 转换字符串被截断为从开始起的长度为precision的字符串。(宽度填充是在精度设置以后完成的.)
    * type-char 它不是强制关联参数的类型,而只是说明某些与类型有关的设置。
          Type-Char                        意义                                 对流起的作用
          p or x                     十六进制                              十六进制输出
          o                             八进制输出                             设置八进制输出
          e                         浮点数科学计数法                     设置浮点数科学计数法
          f                         浮点数定长显示                        设置浮点数定长表示
          g                       浮点数默认的普通表示            回复默认表示
          X, E or G     和他们对应的小写字母意义相同, 
          d, i or u                 十进制输出                         十进制输出
          s or S                 字符串输出                           
           c or C              单字符输出                            只使用转换字符串的首字符
          %                     输出字符%    

 
新增的格式说明

    *添加居中对齐和内部对齐
    * %{nt} , n是一个正整数, 插入n个t制表符。它表示,如果字符串不足n个字符的话,填充字符到n个字符。
    * %{nTX} 同样方式插入制表符, 但将X作为默认制表符
   
    与printf的对比
    假设你有连个变量 x1, x2 (printf支持的内置类型),和一个在printf中使用的格式字符串 s  :   printf(s, x1, x2);
在大多数情况下,它和 cout << format(s) % x1 % x2;的输出是一样的。但是,由于有极少数格式说明符不容易被引进到boost.format中所以使用boost.format模拟printf输出是存在一些缺陷。在很多情况下,这些不支持的选项是可以忽略,所以printf的格式字符串一直使用于boost.format中并产生相同的结果。

下面就列举了所有这些不同的地方 :

    * '0' and ' ' options : printf 对于非数值转换,printf忽视此选项,但是format将他们应用于所有类型。
    * 对printf而言,精度和整数参数一起使用有特殊的含义 :
      printf( "(%5.3d)" , 7 ) ; prints ? ( 007) ?
     而format此时忽略精度选项
    *  printf '选项在format中不起作用。
    * 对星号使用宽度或精度,此选项在printf中用来读取参数的字段(现在已不支持)。
   
    另外, 注意到特殊的 'n' 类型说明符 (用来告诉printf,将输出的字符数存储在一个变量中)在format中不起作用。因此他们输出的结果还是相同的。要获取boost.format输出的字符数,可以使用size()成员函数:
    format formatter( " %+5d " );
    cout 
<<  formatter  %  x;
    unsigned 
int  n  =  formatter.size();

用户自定义类型的输出

所有对stream的状态进行修改的标志将递归作用于用户自定义类型中。如:对于一个Rational类,我们可能写如下代码:
    Rational ratio( 16 , 9 );
    cerr 
<<  format( " %#x  " )   %  ratio;   //  -> "0x10/0x9  "


对于其他选项又有所不同。举个例子说,设置的宽度选项将会作用于对象最终的输出,而不是在内部内部起作用,很幸运是这样的。
 
    cerr  <<  format( " %-8d " )   %  ratio;   //  -> "16/9    "      and not    "16      /9       "
    cerr  <<  format( " %=8d " )   %  ratio;   //  -> "  16/9  "      and not    "   16   /    9   "


但是0 和' '选项也是同样的 ( '+'直接设置流的状态为显示符号. 与'+'不同,流中没有与0和' '对应的状态符号),它的输出看起来就不是很自然:
   
cerr  <<  format( " %+08d  " )   %  ratio;   //  -> "+00016/9"
    cerr  <<  format( " % 08d  " )   %  ratio;   //  -> "000 16/9"


可以通过设计Rational的<<操作符来处理流的宽度,对齐方式等参数以得到更好的输出结果。

操纵器,流的内部状态
format内部使用的流的状态在输出一个参数前被保存起来,在输出后又被恢复。所以对状态的修改只对一个参数起作用。流默认使用的是标准状态:精度6,宽度0,右对齐,十进制表示。
format内部流的状态可以被和参数一起输入的操纵器修改; 通过使用group函数, 如 :
   
cout  <<  format( " %1% %2% %1% " %  group(hex, showbase,  40 %   50 // 输出 "0x28 50 0x28 "



当在一个group中传入N项时, Boost.format需要将操纵器和参数区分处理,因此使用group 应该遵守下列法则 :
   1. 被输出的对象应该作为group中的最后一项。
   2. 前面N-1项被当作manipulators, 他们如果产生输出,输出不会被处理。

每次处理后面的参数时,这些manipulators会传给stream. 在format-string内部的格式选项会被manipulators改写. 例如,在下列代码中,, 十六进制manipulator 比 format-string中的d 类型说明符(十进制格式符)的优先级更高:
   
cout  <<  format( " %1$d %2% %1% " %  group(hex, showbase,  40 %   50 ;
    
//  prints "0x28 50 0x28 "



异常

Boost.format 在格式化对象的使用时,执行一系列的规则.  format-string必须遵循上述语法, 在输出最终字符串是,用户提供的参数数目必须正确。
当format检查到上述规则没有被满足是,它会抛出一个异常,如果进行不处理,就会产生错误。
但是用户可以根据自己的需要进行修改,并根据错误类型使用下列函数抛出异常 :

    unsigned char exceptions(unsigned char newexcept); // query and set
    unsigned char exceptions() const;                  // just query

用户可以通过合并下列选项来设置参数newexcept
    * boost::io::bad_format_string_bit 只抛出format-string不正确格式错误。
    * boost::io::too_few_args_bit 只抛出所有参数都被传入前就查询结果字符串的错误。
    * boost::io::too_many_args_bit 只抛出传入参数过多错误。
    * boost::io::out_of_range_bit 只抛出下标越界错误
    * boost::io::all_error_bits 可以用来抛出所有错误
    * boost::io::no_error_bits 不抛出任何错误.

举个例子,如果你不希望boost.format检查到参数个数不对,你可以定义一个函数,在这个函数内创建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%  " %   1   %   2   %   3   %   4   %   5 ;


如果我们在提供所有参数之前查询结果,对应部分将不会显示。

   
cout  <<  my_fmt( "  _%2%_ _%1%_  " %   1  ;

    // 显示      " __ _1_ /n"

性能事项

在格式化排序输出一些内置类型参数时,可以将boost::format和printf的执行进行比较,也可以和在流上的手工操作进行比较,从而估计出boost::format的性能。比较结果可能与编译器、标准库的实现、选择的format-string以及参数有关。

由于stream普通的执行底层都是调用printf系列函数来进行实际格式化,一般而言,printf将会明显比stream的直接操作要快。由于重新排序产生的消耗,stream的直接操作要比boost::format快(boost::fromat的花费的时间是stream的2-5倍)。

当迭代格式化成为性能的瓶颈时,通过解析format-string并存储在format对象内部,并在使用时复制给其他format对象,性能可以轻微地提高,就像以下所示。
        const boost::format fmter(fstring);
        dest << boost::format(fmter) % arg1 % arg2 % arg3 ;

为了检测出性能对比结果,作者通过4种方法来测量迭代格式化的执行时间。
   1. printf
   2. stream的手工输出
   3. boost::format 用以上复制常量的方式
   4. 直接使用 boost::format

实验使用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模式:
    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 模式 :
    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_t;
      typedef typename string_t::size_type     size_type;
      basic_format(
const charT* str);
      basic_format(
const charT* str, const std::locale & loc);
      basic_format(
const string_t& s);
      basic_format(
const string_t& s, const std::locale & loc);
      basic_format
& operator= (const basic_format& x);

      
void clear(); // reset buffers
      basic_format& parse(const string_t&); // clears and parse a new format string

      string_t 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
 

基本原理

format类的宗旨是为了提供一个更佳的、C++方式的、类型安全的、类型可扩展的、使用streams的printf等价物。
    * 支持定位参数 (需要国际化支持)。
    * 不限制参数的个数。
    * 使格式化命令看起来更直观。
    * 支持利用控制器来修改参数的显示方式。 这是对format-string 语法的一种补充。
    * 可以接受任何类型的变量, 如何转换为字符串将取决于stream. 这一点关系到用户自定义类型。
    * 在printf兼容的基础上, 支持类型安全和类型可扩展特性.

在设计过程中,出现过很多事情,并且做出了很多决定,这些决定在直观上可能并不正确,但是他们都是处于多种原因的考虑而被选择的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值