The Boost Format library<boost/format.hpp> 中的format類提供了類'printf'的格式化,它以類型安全的方式實現允許輸出用戶自定義的類型。
一個format對像從一個格式化字符串構造,它以重複的%操作符給出參數。接著每個參數轉換成字符串,它們被按照格式合成一個字符串。
cout << boost::format("writing %1%, x=%2% : %3%-th try") % "toto" % 40.23 % 50;
// prints "writing toto, x=40.230 : 50-th try"
cout << format("%2% %1%") % 36 % 77 )
或者遲些,像
format fmter("%2% %1%");
fmter % 36; fmter % 77;
你將變量傳送給格式化器。// 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 );
using namespace std;
using boost::format;
using boost::io::group;
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"
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"
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"
cout << format("_%1%_ %1% \n") % group(showpos, setw(5), 101);
在每個 %1% 出現時都要應用操縱子, 輸出 : "_ +101_ +101 \n"
對於一些 std::vector names, surnames, 和 tel (見 sample_new_features.cpp) 輸出 :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
程序 sample_formats.cpp 示例了format的一些簡單應用。
sample_new_features.cpp
舉例說明新增的格式化特徵,它們以prinft的語法格式,比如簡單定位,中間對齊和製表符功能。
sample_advanced.cpp
舉例說明format高級特性,比如重用,修改格式化對像等等。
以及 sample_userType.cpp 示例了對於用戶自定義類型中format庫的行為。
boost::format( format-string ) % arg1 % arg2 % ... % argN
格式化字符串包含的字符文本,在它當中特殊的指示器將被字符串所替換,這些字符串由所給的參數產生。
從c,c++世界繼承來的語法其中之一是使用printf,這樣format能直接使用printf的格式化字符串,並且產生一樣的結果(幾乎包含所有方面,細節請查看 Incompatibilities with printf)。
這些核心語法已被擴展,來允許新特性,但是同時適合c++流的上下文。所以,format接受在格式化字符串中的一系列的指示:
Boost.format所支持的格式化規則嚴格按照Unix98的文檔 Open-group
printf,而非標準c的printf,它不支持有位置信息的參數。(普通的標誌在前兩者中有同樣的含義,這樣都不會任何人帶來麻煩)。
注意:在同一個格式化字符串中同時使用有位置信息(e.g. %3$+d)的格式化規則和沒有位置信息(e.g. %+d)的格式化規則是會出錯的。
在Open-group規則中,使用同一參數多次(e.g. "%1$d %1$d")會導致未定義的行為。Boost.format在這些情況中允許多次使用同一參數。唯一的約束是它期望準確的參數個數,P是格式化用到的最大參數個數(e.g., for "%1$d %10$d", P = 10)。
如果提供多餘或者少於P數量的參數會引起異常(除非它被另外設定,查看 exceptions異常 一節)
一個規範的spec有這樣的格式: [ N$ ] [
flags ] [ width ] [ . precision ]
type-char
方括號中的參數是可選的。下面將一一介紹每個參數域:
標誌 含義 在內部stream的作用 '-' 左對齊 N/A (作用於後面的string) '=' 居中對齊 N/A (作用於後面的string)
- 注意 : 新增的特性,printf中是沒有的 -'_' 內部對齊 設置內部對齊
- 注意:新增特性,printf中沒有 -'+' 顯示符號無論是否正數 設置showpos '#' 顯示數字的基,和小數點 設置showbase 和showpoint '0' 後加0 (插在符號或者基指示器後) 如非左對齊, 調用setfill('0') 並且設置internal
當stream轉換成 user-defined output 後執行額外的操作.' ' 如果字符串不是以+,-開頭,在已轉換的字符串前插入空格 N/A (作用於後面的string)
不同於printf的行為:它不受內部對齊的影響
字符類型 含義 對stream的作用 p or x 十六進制輸出 設置 hex o 十進制輸出 設置 oct e 科學計數浮點格式 設置浮點域位為 scientific f 固定浮點格式 設置浮點域位為 fixed g 默認浮點格式 所有浮點域位歸位 X, E or G 跟小寫一樣,但是使用大寫字符作為輸出(exponents, hex digits, ..) 除了大寫,跟小寫‘x’,‘e’,‘g’一樣 d, i or u decimal 類型輸出 設置進制位為 dec s or S 字符串輸出 精度格式歸位,它的值傳給內部域並作用後面的truncation(查看前面的precision註釋) c or C 單字符輸出 只使用轉換字符串的第一個字符 % 輸出字符 % N/A
注意‘n’類型規則被忽略(相關的參數也一樣),因為它不符合這裡的內容。同樣的,printf的‘l’,‘L’或‘h’(來表示寬度、long或short類型)支持修改符(對於內部流沒有影響)。
printf(s, x1, x2);
cout << format(s) % x1 % x2;
但是因為有些printf格式化規則沒有對應到stream格式化選項中,Boost.format與printf比有一些明顯的不足之處。無論如何,format類應該悄悄的忽略所有不支持的選項,這樣printf格式化串總是能被format接受並輸出幾乎和printf一樣的結果。
format formatter("%+5d");
cout << formatter % x;
unsigned int n = formatter.size();
對stream上所有標誌的更改都會遞歸的作用在用戶自定義的類上。(標誌將保持活動狀態,同時期望的format選項也一樣,因為’<<’操作可能被用戶自定義的類調用)
比如:有一個Rational類,我們能這樣使用:Rational ratio(16,9);
cerr << format("%#x \n") % ratio; // -> "0x10/0x9 \n"
對於其他的格式化選項是另外一回事。比如,對於設置寬度應用於對像產生的最終輸出,並非每個內部輸出,這是幸運的:
cerr << format("%-8d") % ratio; // -> "16/9 " and not "16 /9 "
cerr << format("%=8d") % ratio; // -> " 16/9 " and not " 16 / 9 "
0和' '選項也是一樣(對應於’+’它用showpos直接改變stream的狀態。但是printf沒有相應於0和空格的選項)那樣不是很自然:
cerr << format("%+08d \n") % ratio; // -> "+00016/9"
cerr << format("% 08d \n") % ratio; // -> "000 16/9"
通過仔細設計Rational的<<操作來通過它自己傳遞流的寬度,對齊和showpos參數獲取更好的行為是可能的。它在 sample_userType.cpp 中有舉例說明。
Format的內部流狀態被預先保存並在參數輸出後復原;因此,修改量不持久且只應用於一個參數。流的默認狀態,以標準狀態,是:精度為6,寬度為0,並且decimal標誌被設定。
通過參數format流內部狀態能被操縱子所修改,通過group函數,像:
cout << format("%1% %2% %1%\n") % group(hex, showbase, 40) % 50; // prints "0x28 50 0x28\n"
當傳遞’group’中的N項,Boost.format需要執行從不同於正則表達式的參數操縱子,因此使用group受到下面的約束:
這樣的操縱子每次在下面的參數前被傳遞給流。注意格式化選項通過以這種形式傳入的重寫了的流狀態修改器被格式化字符串內部所解釋。比如下例所說,格式化字符串中hex控制器擁有比d類型描述更高的優先級,它會設置小數輸出:
cout << format("%1$d %2% %1%\n") % group(hex, showbase, 40) % 50;
// prints "0x28 50 0x28\n"
Boost.format 遵循一套使用格式化對象的規則。格式化字符串遵守上面描述的語法,用戶在最終輸出目標前必須提供準確的參數數,如果使用modify_item 或 bind_arg,選項和參數索引不能超出範圍。
當format發現下面中的一條規則不滿足時,它會引出一個相關的異常,因此錯誤不會被忽略和不被處理。但是用戶可以修改這個行為來滿足他的需要,當異常發生時使用下面的函數來選擇一個錯誤:
unsigned char exceptions(unsigned char newexcept); // query and set
unsigned char exceptions() const; // just query
用戶可以通過使用布爾算術計算來合併下面項從而計算參數 newexcept :
舉例,如果你不想要Boost.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% \n") % 1 % 2 % 3 % 4 % 5;
如果我們在所有參數提供前請求結果,結果的相關部分簡單的為空
cout << my_fmt(" _%2%_ _%1%_ \n") % 1 ;
// prints " __ _1_ \n"
用伴隨重排序的boost.format來格式化一些內建的類型的性能可以跟Posix-printf做比較,等價的流手工操作給出了對代價的測量。最終結果很大程度上取決於編譯器,標準庫實現以及格式化字符串和參數的精度選擇。
由於普通流實現最終是要調用printf家族函數來實現格式化,大致上printf將會比直接流操作更快。這主要是由於在重排開銷(分配空間以保存字符串片段,每項格式化時的流初始化, ..)上,直接的流操作會比 boost::format 快(你可期望比例在 2 到 5 倍或更多)。
當迭代格式化成為性能瓶頸時,可以通過把格式化字符串分析為一個格式化對像和每次格式化時拷貝它來提高性能。如下所示。
const boost::format fmter(fstring);
dest << boost::format(fmter) % arg1 % arg2 % arg3 ;
作為性能結果的例子,作者用四個方法測量了迭代個格式化的時間
這個測試使用g++-
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 mode :
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 mode :
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
這個類的目標是引入一個更好的,c++的,類型安全而且可擴展的等價於printf而能為stream所用的類。
準確的說,format被設計來提供以下的特性:在設計的過程中面對很多爭議,作出了一些選擇。這些未必是直觀上很正確。但是任何一種情況它們被採用總是 some reasons 的。
The author of Boost format is Samuel Krempp. He used ideas from Rüdiger Loos' format.hpp and Karl Nelson's formatting classes.
Revised 02 December, 2006
Copyright © 2002 Samuel Krempp
Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)