c++ cout格式化輸出函數詳解

這篇文章主要講解如何在C++中使用cout進行高級的格式化輸出操做,包括數字的各類計數法(精度)輸出,左或右對齊,大小寫等等。經過本文,您能夠徹底脫離scanf/printf,僅使用cout來完成一切須要的格式化輸入輸出功能(從非性能的角度而言)。更進一步而言,您還能夠在<sstream>、<fstream>上使用這些格式化操做,從而代替sprintf和fprintf函數。爲方便描述,下文僅以cout爲例進行介紹。
 ios

1、綜述

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設置格式通常按以下方式進行:
 函數

  • cout.setf(ios::right | ios::hex); //設置16進制右對齊
  • cout.setf(ios::right, ios::adjustfield); //取消其它對齊,設置爲右對齊

 


setf可接受一個或兩個參數,一個參數的版本爲設置指定的格式,兩個參數的版本中,後一個參數指定了刪除的格式。三個已定義的組合格式爲:性能

  • ios::adjustfield  對齊格式的組合位
  • ios::basefield  進制的組合位
  • ios::floatfield  浮點表示方式的組合位

設置格式以後,下面全部使用cout進行的輸出都會按照指定的格式狀態執行。可是若是在一次輸出過程當中須要混雜多種格式,使用cout的成員函數來處理就顯得很不方便了。STL另提供了一套<iomanip>庫能夠知足這種使用方式。<iomanip>庫中將每一種格式的設置和刪除都進行了函數級的同名封裝,好比fixed函數,就能夠將一個ostream的對象做爲參數,在內部調用setf函數對其設置ios::fixed格式後再返回原對象。此外<iomanip>還提供了setiosflags、setbase、setfill、setw、setprecision等方便的格式控制函數,下文會逐一進行介紹。大多數示例代碼都會使用到<iomanip>,所以默認包含的頭文件均爲:
 ui

#include <iomanip>
#include <iostream>

 

 

2、縮進

將輸出內容按指定的寬度對齊,須要用到ios::right、ios::left、ios::internal和iomanip裏的setw。其中setw用於指定要輸出內容的對齊寬度。如下兩段代碼的結果徹底相同,前面是一個浮點數-456.98,後面緊跟着一個字符串「The End」以及換行符「endl」。

代碼一:
 spa

#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
    cout.flags(ios::left); //左對齊
    cout << setw(10) << -456.98 << "The End" << endl;
    cout.flags(ios::internal); //兩端對齊
    cout << setw(10) << -456.98 << "The End" << endl;
    cout.flags(ios::right); //右對齊
    cout << setw(10) << -456.98 << "The End" << endl;
    return 0;
}

 

 

代碼二:
 代碼規範

#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
    cout << left << setw(10) << -456.98 << "The End" << endl; //左對齊
    cout << internal << setw(10) << -456.98 << "The End" << endl; //兩端對齊
    cout << right << setw(10) << -456.98 << "The End" << endl; //右對齊
    return 0;
}

 

 

結果:
-456.98   The End
-   456.98The Endcode

   -456.98The End對象

這裏要額外說明的一點是,setw函數會用當前的填充字符控制對齊位置,默認的填充字符是空格。能夠經過<iomanip>的setfill來設置填充字符,好比下面的代碼用字符「0」做爲填充字符:ip


#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
    cout << setfill('0') << setw(10) << 45698 << endl;
    return 0;
}
 
結果:0000045698

 

 

3、整數

輸出整數的格式有按不一樣進制數出:ios::hex(16進制)、ios::dec(10進制)、ios::oct(8進制),也可強制其輸出符號(正數也加上「+」號前綴),對於16進制的輸出還可配合ios::uppercase使全部字母以大寫表示。代碼示例以下:

#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
    cout.setf(ios::showpos | ios::uppercase);
    cout << hex << setw(4) << 12 << setw(12) << -12 << endl;
    cout << dec << setw(4) << 12 << setw(12) << -12 << endl;
    cout << oct << setw(4) << 12 << setw(12) << -12 << endl;
    cout.unsetf(ios::showpos | ios::uppercase);
    cout << hex << setw(4) << 12 << setw(12) << -12 << endl;
    cout << dec << setw(4) << 12 << setw(12) << -12 << endl;
    cout << oct << setw(4) << 12 << setw(12) << -12 << endl;
    return 0;
}
 


結果:


   C    FFFFFFF4
 +12         -12
  14 37777777764
   c    fffffff4
  12         -12
  14 37777777764

利用<iomanip>的setbase函數一樣能夠設置整數的三種進制,參數分別爲八、10和16,但使用起來比上面的方法還更復雜一些,除非是特殊的代碼規範要求(有些規範要求避免將常量直接做爲表達式),通常不建議使用setbase。此外,還能夠利用ios::showbase來爲整數的前面加一個表示進制的前綴,代碼以下:

#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
    cout << showbase << setw(4) << hex << 32 << setw(4) << oct << 32 << endl;
    cout << noshowbase << setw(4) << hex << 32 << setw(4) << oct << 32 << endl;
    return 0;
}
 

結果:

0x20 040
  20  40
上面代碼中的showbase/noshobase也能夠用cout的setf來代替,其結果是徹底相同的:


#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
    cout.setf(ios::showbase);
    cout << setw(4) << hex << 32 << setw(4) << oct << 32 << endl;
    cout.unsetf(ios::showbase);
    cout << setw(4) << hex << 32 << setw(4) << oct << 32 << endl;
    return 0;
}
 

 

 

 

4、小數

小數可分爲兩種格式類型,一種是定點表示「ios::fixed」(不帶指數域),另外一種是科學計數法表示「ios::scientific」(帶指數域)。與<iomanip>的setprecision配合使用,能夠表示指定小數點後面的保留位數(四捨五入)。示例代碼以下:

#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
    cout.setf(ios::fixed);
    cout << setprecision(0) << 12.05 << endl;
    cout << setprecision(1) << 12.05 << endl;
    cout << setprecision(2) << 12.05 << endl;
    cout << setprecision(3) << 12.05 << endl;
    cout.setf(ios::scientific, ios::floatfield);
    cout << setprecision(0) << 12.05 << endl;
    cout << setprecision(1) << 12.05 << endl;
    cout << setprecision(2) << 12.05 << endl;
    cout << setprecision(3) << 12.05 << endl;
    return 0;
}
 
結果:
12
12.1
12.05
12.050
1.205000e+001
1.2e+001
1.21e+001
1.205e+001

須要注意的是,有時會由於機器的精度問題致使四捨五入的結果不正確。這種問題通常須要手動修正,見以下代碼示例:

#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
    cout << fixed << setprecision(1) << 2.05 << endl;
    cout << fixed << setprecision(1) << 2.05 + 1e-8 << endl;
    return 0;
}
 結果:
2.0
2.1

 

 

4、字符串

字符串的輸出處理主要是對齊,這一點在第二部分已經介紹過了,下面主要介紹字符串的輸入方法。爲了方便起見,咱們使用<string>庫。在輸入字符串時,能夠利用<string>庫提供的getline函數讀取整行數據。getline函數有兩個版本,第一個版本有兩個參數,第一個參數指定輸入流(好比cin),第二個參數指定一個string對象。getline會讀取屏幕上輸入的字符,直到遇到換行符「\n」爲止;第二個版本有三個參數,前兩個與第一個版本相同,第三個參數爲指定的結束字符。注意,getline不會讀入默認或指定的結束字符,但在調用以後讀取的位置已經跳過結束字符。調用示例代碼以下:

#include <iomanip>
#include <iostream>
#include <string>
using namespace std;
int main(void) {
    string str1, str2;
    getline(cin, str1);
    cin >> str2;
    cout << str1 << endl << str2 << endl;
    return 0;
}
 
輸入:
   abc
   abc
結果:
   abc
abc

 

 

 

5、緩衝區

因爲調用系統函數在屏幕上逐個顯示字符是很慢的,所以cin/cout爲了加快速度使用緩衝區技術,粗略的講就是暫時不輸出指定的字符,而是存放在緩衝區中,在合適的時機一次性輸出到屏幕上。若是單純使用C++的輸入/輸出流來操做字符是不存在同步的問題的,可是若是要和C標準庫的stdio庫函數混合使用就必需要當心的處理緩衝區了。若是要與scanf和printf聯合使用,務必在調用cout前加上cout.sync_with_stdio(),設置與stdio同步,不然輸出的數據順序會發生混亂。

flush和endl都會將當前緩衝區中的內容當即寫入到屏幕上,而unitbuf/nounitbuf能夠禁止或啓用緩衝區。示例代碼以下:

#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
    cout << 123 << flush << 456 << endl;
    cout << unitbuf << 123 << nounitbuf << 456 << endl;
    return 0;
}
 
結果:
123456
123456

 

 

 

6、綜合使用

示例代碼: #include <iomanip> #include <iostream> #include <string> using namespace std; struct COMMODITY { string Name; int Id; int Cnt; double Price; }; int main(void) {     COMMODITY cmd[] = {         {"Fruit", 0x101, 50, 5.268},         {"Juice", 0x102, 20, 8.729},         {"Meat", 0x104, 30, 10.133},     };     cout << left << setw(8) << "NAME" << right << setw(8) << "ID";     cout << right << setw(8) << "COUNT" << right << setw(8) << "PRICE" << endl;     for (int i = 0; i < sizeof(cmd) / sizeof(cmd[0]); ++i) {         cout << left << setw(8) << cmd[i].Name;         cout << right << hex << showbase << setw(8) << cmd[i].Id;         cout << dec << noshowbase << setw(8) << cmd[i].Cnt;         cout << fixed << setw(8) << setprecision(2) << cmd[i].Price << endl;     }     return 0; }   結果: NAME          ID   COUNT   PRICE Fruit      0x101      50    5.27 Juice      0x102      20    8.73 Meat       0x104      30   10.13

相關文章
相關標籤/搜索