這篇文章主要講解如何在C++中使用cout進行高級的格式化輸出操作,包括數字的各種計數法(精度)輸出,左或右對齊,大小寫等等。通過本文,您可以完全脫離scanf/printf,僅使用cout來完成一切需要的格式化輸入輸出功能(從非性能的角度而言)。更進一步而言,您還可以在<sstream>、<fstream>上使用這些格式化操作,從而代替sprintf和fprintf函數。爲方便描述,下文僅以cout爲例進行介紹。
一、綜述
cout是STL庫提供的一個iostream實例,擁有ios_base基類的全部函數和成員數據。進行格式化操作可以直接利用setf/unsetf函數和flags函數。cout維護一個當前的格式狀態,setf/unsetf函數是在當前的格式狀態上追加或刪除指定的格式,而flags則是將當前格式狀態全部替換爲指定的格式。cout爲這個函數提供瞭如下參數(可選格式):
- ios::dec 以10進製表示整數
- ios::hex 以16進製表示整數
- ios::oct 以8進製表示整數
- ios::showbase 爲整數添加一個表示其進制的前綴
- ios::internal 在符號位和數值的中間插入需要數量的填充字符以使串兩端對齊
- ios::left 在串的末尾插入填充字符以使串居左對齊
- ios::right 在串的前面插入填充字符以使串居右對齊
- ios::boolalpha 將bool類型的值以true或flase表示,而不是1或0
- ios::fixed 將符點數按照普通定點格式處理(非科學計數法)
- ios::scientific 將符點數按照科學計數法處理(帶指數域)
- ios::showpoint 在浮點數表示的小數中強制插入小數點(默認情況是浮點數表示的整數不顯示小數點)
- ios::showpos 強制在正數前添加+號
- ios::skipws 忽略前導的空格(主要用於輸入流,如cin)
- ios::unitbuf 在插入(每次輸出)操作後清空緩存
- ios::uppercase 強制大寫字母
以上每一種格式都佔用獨立的一位,因此可以用“|”(位或)運算符組合使用。調用setf/unsetf或flags設置格式一般按如下方式進行:
1
2
|
cout.setf(ios::right
| ios::hex); //設置16進制右對齊 cout.setf(ios::right,
ios::adjustfield); //取消其它對齊,設置爲右對齊
setf可接受一個或兩個參數,一個參數的版本爲設置指定的格式,兩個參數的版本中,後一個參數指定了刪除的格式。三個已定義的組合格式爲:
設置格式之後,下面所有使用cout進行的輸出都會按照指定的格式狀態執行。但是如果在一次輸出過程中需要混雜多種格式,使用cout的成員函數來處理就顯得很不方便了。STL另提供了一套<iomanip>庫可以滿足這種使用方式。<iomanip>庫中將每一種格式的設置和刪除都進行了函數級的同名封裝,比如fixed函數,就可以將一個ostream的對象作爲參數,在內部調用setf函數對其設置ios::fixed格式後再返回原對象。此外<iomanip>還提供了setiosflags、setbase、setfill、setw、setprecision等方便的格式控制函數,下文會逐一進行介紹。大多數示例代碼都會使用到<iomanip>,因此默認包含的頭文件均爲:
二、縮進將輸出內容按指定的寬度對齊,需要用到ios::right、ios::left、ios::internal和iomanip裏的setw。其中setw用於指定要輸出內容的對齊寬度。以下兩段代碼的結果完全相同,前面是一個浮點數-456.98,後面緊跟着一個字符串“The End”以及換行符“endl”。
代碼一:
代碼二:
結果: -456.98 The End
這裏要額外說明的一點是,setw函數會用當前的填充字符控制對齊位置,默認的填充字符是空格。可以通過<iomanip>的setfill來設置填充字符,比如下面的代碼用字符“0”作爲填充字符:
結果: 0000045698
三、整數輸出整數的格式有按不同進制數出:ios::hex(16進制)、ios::dec(10進制)、ios::oct(8進制),也可強制其輸出符號(正數也加上“+”號前綴),對於16進制的輸出還可配合ios::uppercase使所有字母以大寫表示。代碼示例如下:
結果: C FFFFFFF4
利用<iomanip>的setbase函數同樣可以設置整數的三種進制,參數分別爲8、10和16,但使用起來比上面的方法還更復雜一些,除非是特殊的代碼規範要求(有些規範要求避免將常量直接作爲表達式),一般不建議使用setbase。此外,還可以利用ios::showbase來爲整數的前面加一個表示進制的前綴,代碼如下:
結果: 0x20 040
上面代碼中的showbase/noshobase也可以用cout的setf來代替,其結果是完全相同的:
四、小數小數可分爲兩種格式類型,一種是定點表示“ios::fixed”(不帶指數域),另一種是科學計數法表示“ios::scientific”(帶指數域)。與<iomanip>的setprecision配合使用,可以表示指定小數點後面的保留位數(四捨五入)。示例代碼如下:
結果: 12
需要注意的是,有時會因爲機器的精度問題導致四捨五入的結果不正確。這種問題一般需要手動修正,見如下代碼示例:
結果: 2.0
四、字符串字符串的輸出處理主要是對齊,這一點在第二部分已經介紹過了,下面主要介紹字符串的輸入方法。爲了方便起見,我們使用<string>庫。在輸入字符串時,可以利用<string>庫提供的getline函數讀取整行數據。getline函數有兩個版本,第一個版本有兩個參數,第一個參數指定輸入流(比如cin),第二個參數指定一個string對象。getline會讀取屏幕上輸入的字符,直到遇到換行符“\n”爲止;第二個版本有三個參數,前兩個與第一個版本相同,第三個參數爲指定的結束字符。注意,getline不會讀入默認或指定的結束字符,但在調用之後讀取的位置已經跳過結束字符。調用示例代碼如下:
輸入: abc
結果: abc
五、緩衝區由於調用系統函數在屏幕上逐個顯示字符是很慢的,因此cin/cout爲了加快速度使用緩衝區技術,粗略的講就是暫時不輸出指定的字符,而是存放在緩衝區中,在合適的時機一次性輸出到屏幕上。如果單純使用C++的輸入/輸出流來操作字符是不存在同步的問題的,但是如果要和C標準庫的stdio庫函數混合使用就必須要小心的處理緩衝區了。如果要與scanf和printf聯合使用,務必在調用cout前加上cout.sync_with_stdio(),設置與stdio同步,否則輸出的數據順序會發生混亂。 flush和endl都會將當前緩衝區中的內容立即寫入到屏幕上,而unitbuf/nounitbuf可以禁止或啓用緩衝區。示例代碼如下:
結果: 123456
六、綜合使用示例代碼:
結果: NAME ID COUNT PRICE
|