sprintf
_snprintf(snprintf)
std::stringstream
std::strstream
boost::lexical_cast
boost::format
cstring::format
1
.
sprintf
使用
sprintf
不安全,輕則破壞資料的準確性,重則程式崩潰。請看下面的程式片段:
cstring
strtest
= "hello"
;
1inta=
10;2
char
tbuf[10
];
3sprintf
(tbuf
, "%s,%d"
, "0123456789 0123456789 0123456789 0123456789",
a); 4
printf
("%s/n"
, tbuf
); 5if
(10== a
)
6strtest
.insert(0
, "abc"
);
7printf
("%s/n"
, strtest
.getbuffer(0
));
編譯一下,沒問題,
0 error(s), 0 warning(s)。
看看輸出來的結果,很幸運,執行第
4行輸出
tbuf
格式的字串,跟想要的結果一致的。但奇怪的問題出現了,
a的數值只有初始化賦值為
10,沒其他地方給它賦值了,為宣告不會執行第
6行,更奇怪的是執行第
7行拋異常了。其實原因就是,
sprintf,
在幕後偷偷的修改了
a的值,並且破壞了
strtest
這個物件。
類似的錯誤還有想這樣的**
:
inti, a
[10];
for(i=
0; i<=
10; ++i)
a[i] = 0;
本來想給陣列
a初始化,沒想到的是,程式進入了乙個死迴圈。
i (a[10])
0x0012ff54
a[9]
0x0012ff50
a[8]
0x0012ff4c
a[7]
0x0012ff48……
通常情況下,棧是往記憶體的低位址方向增長的,也就是說,現壓棧的內容存放在高位址區域,後壓棧的內容存放在低位址區域。
2
.
_snprintf
把
sprintf
處的**改為用
_snprintf,
看看結構如何。
_snprintf
(tbuf
,sizeof(
tbuf
)-1,
"%s,%d"
, "0123456789 0123456789 0123456789 0123456789",
a);
我們一看結果就知道是緩衝區不夠了,這樣容易發現問題,也不會破壞程式記憶體。注意,即便這樣,仍然還存在另外一種出錯的可能,即呼叫者將緩衝區的長度搞錯了。
3
.
std::stringstream
ostringstream temp;
temp<<"0123456789 0123456789 0123456789 0123456789," sprintf 和std::stringstream 的優缺點: 1) .sprintf 具有易用性和清晰性,而 std::stringstream 比較臃腫了,至少要三行**實現。 2) .sprintf 位元組利用緩衝區,效率高。而 std::stringstream 要儘管多次拷貝,有可能還要多次動態分配記憶體。 3) .std::stringstream 動態分配記憶體,所以長度是安全的,不會出現破獲記憶體的情況 4) .std::stringstream 模版型別安全,方便。而 sprintf 有可能把乙個整數格式到 %s4.std::strsteam char tbuf[10 ]; std:: strstream temp (tbuf , sizeof (tbuf )); temp<<"0123456789 0123456789 0123456789 0123456789,"<效果跟 _snprintf 差不多,但是它要自己手動加上 『/0』 字串結束符。而且至少要寫兩行**。優點也是長度安全,型別安全,有模版的親和性。我個人來說不喜歡用。 5.boost::lexical_cast strings= "0123456789 0123456789 0123456789 0123456789,"+ boost ::lexical_cast < string >( a); 實際上lexical_cast 的目的只是為了將資料從乙個可流化的型別轉換為另乙個可流化的型別。就類似 atoi, itoa, 等之類的函式。做格式化一串資料感覺就不是很好了。 6.boost::format boost ::format fs= boost ::format ("%1%,%2%" ) %"0123456789 0123456789 0123456789 0123456789"% a ; //string ss = fs.str(); boost ::format 簡潔優雅,長度安全,型別安區。只是由於記憶體是動態分配的,效率上比 sprintf, _snprintf 稍低一點。但如果我們的專案使用了 boost 庫,用boost::format 總體上比 sprintf,_snprintf 都好一點。 7mfc 中的cstring 如果決定用 mfc庫,並且以後不會移植的話,最好就用 cstring。 cstrings; s.format ("%s,%d" , "0123456789 0123456789 0123456789 0123456789", a ); 但是它要表明格式的型別和 sprintf, _snprintf 一樣,型別並不是總是安全的。 總結:通常情況下考慮效率使用 _snprintf 最好,如果使用 boost 庫,且對執行效率沒有苛刻的要求請使用 boost ::format ,如果使用了 mfc,且又不考慮以後的移植,請使用 cstring。 補充:我們經常對字串的拷貝同樣有兩 c標準函式 : char *strcpy( char * strdest, const char *strsource ); char *strncpy( char *strdest, const char *strsource, size_t count ); 萬一我們的 strdest 緩衝區長度不夠,同樣會破壞記憶體,使程式崩潰。 例如:char tbuf[10 ]=; string strsrc = "hello, i'm string" ; strcpy (tbuf , strsrc .c_str ());改為 strncpy (tbuf , strsrc .c_str (), min( sizeof (tbuf)-1 ,strsrc .size ())); 那就安全了。 上面這些都是一些自己在開發過程中的總結和心得,希望對朋友們有幫助! 通常在使用字串的時候,會對字串進行格式化,然後輸出或呼叫 一般我們使用替換標記對字串進行格式化 string str1 string.format add is 1,2,3 而且在c 中的替換標記可以以任意順序和次數出現在格式化字串中,但替換值是按順序排的,而且替換標記不能超出索引範圍 string... 例如 string s hello map.put target world string res format s,map 有什麼用呢?比如在some.properties中配置模板字串,但是如果用 這種方式,在配置了spring讀取properties注入變數的時候,這個變數就找不到會報錯。這個... 格式化字元 格式化字元 描述 f或f 格式化浮點數 e或e 用指數計數法格式化數字 p或p 格式化百分數 n或n 用逗號分隔符格式化數字 c或c 格式化本地貨幣值 d或d 格式化十進位制數 g或g 用浮點數或指數計數法格式化數字 x或x 將浮點數轉化為十六進製制 格式化變數中0指第乙個變數,6表示顯...格式化字串
字串格式化
格式化字串