Boost Libraries: Format

在傳統的 C 語言當中,處理 output 的問題我們會用 printf 這個 function。藉由 printf 的格式化輸出,可以方便的做些簡單的排版輸出。等到進入了 C++ 時代,cout 取代了 printf,但是卻失去了原本好用的格式化輸出,使得有時候處理 output 會變成一件繁瑣的事情。boost::format 就是為了這個而生。

Synopsis
一個 format 物件是經由 format-string 和其後的參數建構而來,後面傳進來的參數是以 operator % 來做連接的。每一個參數都會被轉型成為 string 的型式,接著在依照 format-string 的樣子轉換成一個 string。下面是一個例子:
cout << boost::format("writing %1%,  x=%2% : %3%-th try") % "toto" % 40.23 % 50; 
     // prints "writing toto,  x=40.230 : 50-th try"

How it works
  1. 當你呼叫 format(s) 時,它將會建構一個 format 物件,並且 parse 其中的 format string 給下一個步驟使用。
  2. 接著,可能是馬上,就像
    cout << format("%2% %1%") % 36 % 77 ;
    
    或是稍後,就像
    format fmter("%2% %1%");
    fmter % 36; fmter % 77;
    
    你把變數「餵」到 format 當中,這些變數會變成所謂的 internal stream,它們的 state 是由之前的 format-string 所決定的,接著 format string 將會儲存這些結果給下一步使用。
  3. 當所有的參數都被餵進去之後,你可以把 format dump 到一個 stream 當中,或是透過 member function str() 轉成一個 string。如例子:
    // 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 );
    
    
  4. 另外,你還可以重新使用用過的 format object,來降低處理的複雜度。

Examples
  • 簡單的 output,有 re-ordering:
    cout << format("%1% %2% %3% %2% %1% \n") % "11" % "22" % "333"; // 'simple' style.
    
    將會輸出 "11 22 333 22 11 \n"
  • 更精確的格式輸出:
    cout << format("(x,y) = (%1$+5d,%2$+5d) \n") % -23 % 35;     // Posix-Printf style
    
    會輸出:"(x,y) = ( -23, +35) \n"
  • 傳統的 printf 語法,沒有 re-ordering:
    cout << format("writing %s,  x=%s : %d-th step \n") % "toto" % 40.23 % 50; 
    
    輸出:"writing toto, x=40.23 : 50-th step \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;
    
    這些都代表著:"(x,y) = ( -23, +35) \n"
  • 使用 manipulators 去修改 format-string
    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 ;
    
    都是代表著 "_ +101_ 101 \n"
  • 使用有參數的 manipulators:
    cout << format("_%1%_ %1% \n") % group(showpos, setw(5), 101);
    
    manipulators 會在每個有 %1% 的地方作用,因此會輸出 "_ +101_ +101 \n"
  • 新的 format features:"absolute tabulations",在迴圈當中相當有用,可以確定一個欄位在每一行都是輸出在同一個位置。
    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
    
其他的部分可以在 boost format 的 document 當中看到,不過大概看過這些就可以應用了。