1. 程式人生 > >boost的字串處理函式——format

boost的字串處理函式——format

boost::format的格式一般為:
    boost::format( "format-string ") % arg1 % arg2 % ... % argN ;
    注意這裡沒有示例物件,format-string代表需要格式化的字串,後面用過載過的%跟引數

1.format佔位符的使用 (佔位符 是 從 1 開始的,不是從 0 開始的)
    std::cout<<boost::format("%1% \n %2% \n %3%" )%"first"%"second"%"third";
    上面例子中%X%表示佔位符,%1%就是第一個佔位符,%2%就是第二個,後面類推,
    再後面的%"xxx"就對應著每個佔位符,也就是說如果我們寫成:
    std::cout<<boost::format("%2% \n %1% \n %3%" )%"first"%"second"%"third";

    將會輸出
    second
    first
    third

    當然我們也可以分開寫,比如:
    boost::format fmt("%2% \n %1% \n %3%" );
    fmt %"first";
    fmt %"second";
    fmt %"third";

2.format的C語言形式
    你可以這樣通過變數格式化,這與int a=5;printf("%d",a);是一個道理,不同的是format是對字元的格式化而不包含輸出。
    int a=5;
    int b=6;
    std::cout<< boost::format("%1% %2%" )%a%b;

// 方式一 
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新的格式說明符

            %{nt}
            當n是正數時,插入n個絕對製表符
            cout << boost::format("[t]")  << endl;

            %{nTX}
            使用X做為填充字元代替當前流的填充字元(一般預設是一個空格)
            cout << boost::format("[T*]")  << endl;
    */
    cout<<boost::format("[%5t] Test %5t* ");
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#include <boost/format.hpp>
#include <iomanip>
using namespace boost;
using namespace std;
using boost::io::group;
/*format類摘要
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

*/
/*格式化語法:
 * 1.%05d:輸出寬度為5的整數,不足位用0填充
 * 2.%-8.3f:輸出左對齊,總寬度為8,小數位3位的浮點數
 * 3.%10s:輸出10位的字串,不足位用空格填充
 * 4.%05x:輸出寬度為5的大寫16進位制數,不足位用0填充
 * 5.%|spec|:與printf格式選項功能相同,但兩邊增加了豎線分隔,可以更好地區分格式化選項與普通字元
 * 6.%N%:標記第N個引數,相當於佔位符,不帶任何奇特的格式化選項
 *
 * 建議:
 * 1.format由於要做安全檢查工作,效能略差,約比printf慢2到5倍,為了提高format的效能,建議在程式設計中應該先建立const format物件,然後拷貝這個物件進行格式化操作
 *
 * 高階功能:
 * 1.basic_format& bind_arg(int argN,const T& val); //將第argN個引數繫結為val,即使呼叫clear()也保持不變
 * 2.basic_format& clear_bind(int argN);    //取消格式化字串第argN位置的引數繫結
 * 3.basic_format& clear_binds();   //取消格式化字串所有位置引數繫結,並呼叫argN
 * 4.basic_format& modify_item(int itemN,T manipulator);    //設定格式化字串第itemN位置的格式化選項,manipulator是一個boost::io::group返回的物件
 * */
int main( int argc,char **argv)
{
    cout << format("%s:%d+%d=%d\n")%"sum"% 1%2%(1+2); //經典用法
    cout << format("%1% %2%") % 36 %37; //簡單風格,可以重新排列輸出
    cout << format("(x,y)=(%1$+5d,%2$+5d)\n") %-23 %35;    //精確的格式化,帶posix_printf位置提示符
    format fmt("(%1%+%2%)*%2%=%3%");
    fmt % 2 % 5;
    fmt % ((2+5*5));

    format fmt1("%05d\n%-8.3f\n%10s\n%05x\n");
    cout << fmt1 %62 %2.236 %" 12345678" %48;
    cout << fmt.str() << endl;

    format fmt2("%1% %2% %3% %2% %1%\n");
    cout << fmt2 %1 %2 %3;
    fmt2.bind_arg(2,10);
    cout << fmt2 %1 %3;

    fmt2.clear();
    cout << fmt2 % group(showbase,oct,111) % 333;
    fmt2.clear_binds();

    fmt2.modify_item(1,group(hex,right,showbase,setw(8),setfill('*')));
    cout << fmt2 %49 %20 %100;
     return (0);
}
// 可以延遲使用,順序不必一致
    boost::format fmter("%2% %1%");
    fmter % 36;
    fmter % 77;
    cout << fmter << endl;
    // 輸出:77 36
   
    // 可重用
    fmter % 12;
    fmter % 24;
    cout << fmter << endl;
    // 輸出:24 12
 
    // 可直接轉成字串
    std::string s = fmter.str();
    std::string s2 = str( boost::format("%2% %1% %2% %1%")%"World"%"Hello");
 
    cout << s << endl << s2 << endl;
    // 輸出:
    // 24 12
    // Hello World Hello World
 
    // 可以使用printf指示符
    cout << boost::format("%3.1f - %.2f%%") % 10.0 % 12.5  << endl;
    // 輸出:10.0 - 12.50%
 
    // printf指示符裡使用N$指定使用第幾個引數
    cout << boost::format("%2$3.1f - %1$.2f%%") % 10.0 % 12.5  << endl;
    // 輸出:12.5 - 10.00%
    std::cout << boost::format("%s:%04d%02d%02d") % "日期"% 2013 % 9 % 28 << std::endl;
    std::string test("string");
    std::cout << boost::format("%s") % test<< std::endl;
    
    boost::format fmt1("%1% + %2%*%1% = %3%");
    fmt1 % 2 % 3 % (2+2*3) ;
    std::cout << fmt1.str() << std::endl;
    
    cout << boost::format( "%1% %2%" ) % "Hell" % "Low" << endl;
    string s1 = boost::str( boost::format( "%2% %1%" ) % "Hell" % "Low" );
    cout << s1 << endl;
    wcout << boost::wformat( L"%s %X" ) % L"-1 is" % -1 << endl;
    wstring s2 = boost::str( boost::wformat( L"%2$s %1$.2f" ) % 3.141592 % L"Version" );
    wcout << s2 << endl;
    
    char text[]="hello";
    bool is_all_lower = boost::algorithm::all(text, boost::algorithm::is_lower());
    
    char output[128];
    sprintf(output, "<%s> %s in the lower case", text, (is_all_lower? "is": "is not"));
    cout<<output<<endl;

用boost::format來格式化字串

在字串處理中少不了格式化字串,C++中傳統的格式化函式是C語言的sprintf,但它一個很大的問題就是不安全。因此,在stl中引入了stringstream來實現安全格式化,但是stringstream卻遠不如sprintf來得直觀。例如,對如如下程式碼:

 char text[]="hello";    
 bool is_all_lower = boost::algorithm::all(text, is_lower());
    
 char output[128];
 sprintf(output, "<%s> %s in the lower case", text, (is_all_lower? "is": "is not")); 
如果把最後兩句format的函式用stringstream來寫的話,可讀性是遠不如sprintf的。
stringstream output;
output << "<" << text << "> "<< (is_all_lower)? "is": "is not")<< " in the lower case"; 
boost引入了一個提供類似.net中的string.format的方式提供格式化字串的函式,用它來格式化的話就是如下形式:
boost::format fmt = boost::format("<%s> %s in the lower case") % text % (is_all_lower? "is": "is not");
string output = fmt.str(); 

前面的例子中演示的是C風格的格式化字串,boost.format也提供了類似.net風格的格式化字串方式:

    boost::format fmt = boost::format("<%1%>%2% in the lower case") % text % (is_all_lower?"is": "is not");
    cout << fmt << endl;

這種方式更容易看到引數在格式化字串中的位置,推薦這種形式。不過它的起始座標是1而不是0,用慣了.net的string.format的朋友需要注意下。

格式化控制

格式化語法為: [ N$ ] [ flags ] [ width ] [ . precision ] type-char。也提供了C語言和.net兩種風格。

    //傳統c語言風格
    cout << boost::format("\n\n%s"
            "%1t
十進位制
= [%d]\n"
            "%1t
格式化的十進位制 = [%5d]\n"
            "%1t
格式化十進位制,前補'0' = [%05d]\n"
            "%1t
十六進位制 = [%x]\n"
            "%1t
八進位制 = [%o]\n"
            "%1t
浮點 = [%f]\n"
            "%1t
格式化的浮點 = [%3.3f]\n"
            "%1t
科學計數 = [%e]\n"
            ) % "example :\n" % 15 % 15 % 15 % 15 % 15 % 15.01 % 15.01 % 15.01 << endl;

    //.net
的風格

    cout << boost::format("%1%"
            "%1t
十進位制
= [%2$d]\n"
            "%1t
格式化的十進位制 = [%2$5d]\n"
            "%1t
格式化十進位制,前補'0' = [%2$05d]\n"
            "%1t
十六進位制 = [%2$x]\n"
            "%1t
八進位制 = [%2$o]\n"
            "%1t
浮點 = [%3$f]\n"
            "%1t
格式化的浮點 = [%3$3.3f]\n"
            "%1t
科學計數 = [%3$e]\n"
            ) % "example :\n" % 15 % 15.01 << endl;

異常處理

既然boost.format函式是用來代替sprintf的,那麼自然就得有異常處理的功能,而不是像sprintf那樣死給你看。boost.format的處理方法是拋異常,它在如下兩種情況家會拋異常:

  1. format字串非法
  2. format繫結非法

如下程式碼演示了這兩種情形:

try
    {
        boost::format("<%3");
    }
    catch(std::exception& err)
    {
        cout << err.what() << endl;
    }
    
    boost::format fmt = boost::format("<%3%> %2% in the lower case") % text % (is_all_lower? "is": "is not");
    try
    {
        cout << fmt << endl;
    }
    catch(std::exception& err)
    {
        cout << err.what() << endl;
    }

封裝

boost.format是以一個物件,而不是函式來實現的,導致其使用和異常處理起來要麻煩不少,不過,利用c++11的可變引數模板的語法還是可以很容易把它封裝成一個可變引數的函式的形式:

    string string_fromat(constchar*format, …)

需要定義三個過載版本:

    template<classTFirst>
    void string_format(boost::format&fmt, TFirst&& first)
    {
        fmt % first;
    }

    template<classTFirst,class... TOther>
    void string_format(boost::format&fmt, TFirst&& first,TOther&&... other)
    {
        fmt % first;
        string_format(fmt, other...);
    }

    template<classTFirst,class... TOther>
    string string_format(constchar*format, TFirst&& first,TOther&&... other)
    {
        boost::format fmt(format);
        string_format(fmt, first,other...);
        return fmt.str();
    }

現在就可以這麼用了:

    auto output = string_format("<%1%> %2% in the lower case", text, (is_all_lower? "is":"is not"));

所有的異常也都會在該函式中丟擲,雖然效率上相對低點,但用起來要舒服點。