c++的輸入輸出不是建立在語言上的,而是由iostream
和fstream
文件中定義的一組模板類實現的,且這個類庫不是正式語言定義的組成部分,就是說cin,cout
等并不是語言的關鍵字.
流和緩沖區
c++把輸入輸出看作是字節流,輸入時,程序從輸入流中截取(>>)字符,輸出時,程序將字節插入(<<)到輸出流.通過這樣的方式,流就相當于連接輸入端和輸出端的一個橋梁,兩端只需要關聯到相應的流上即可實現連接,這樣處理輸入輸出的方式將獨立與流的去向.
如下圖所示:
緩沖區
是指用作中介的內存塊,主要作用是用來提高處理輸入輸出的效率.原因在于像磁盤驅動器這樣的設備通常是以512字節的塊為單位來傳輸信息,而程序每次只能處理一個字節.所以通過緩沖的方法,一次從磁盤讀取大量的信息存儲到緩沖區,程序再從緩沖區中每次讀取一個字節,因為從內存中讀取一個字節的時間要遠小于從磁盤讀取的時間.
c++I/O文件
各個I/O類的繼承關系如下圖所示:
ios_basic
類表示流的一般特性,如是否可讀取,是二進制流還是文本流.以及獨立于類型的一些特性.
-
streambuf
類為緩沖區提供內存并提供了用于填充緩沖區,訪問緩沖區內容,刷新緩沖區,管理緩沖區等的方法 -
ios
類繼承與ios_basic
類并且包含了一個指向streambuf
的指針成員. -
ostream
類繼承自ios
類并提供了輸出方法 -
istream
類繼承自ios
類并提供了輸入方法 -
iostream
類多繼承于istream
和ostream
類
在程序中包含iostream
文件將自動創建8個流對象,4個窄字符流,4個寬字符流:
cin
默認關聯標準輸入設備,wcin
與此類似,用于處理wchar_t
類型
-
cout
默認關聯到標準輸出設備,wcout
與此類似,用于處理wchar_t
類型 -
cerr
對應標準錯誤流,默認關聯到標準輸出設備,這個流沒有緩沖機制,wcerr
.... -
clog
也對應標準錯誤流,但是這個流有緩沖機制,wclog
....
如上所知,c++的以上幾個對象就代表了c++的輸入輸出流.所以在流對象中就包含了與輸入輸出有關信息的數據成員,如顯示數據時使用的字段寬度,小數位數等...
重定向
由上面流的概述可知流只是充當連接輸入端和輸出端的一個橋梁而已.所以我們就可以通過改變流的流向來實現輸入輸出的重定向功能.
- 標準輸入重定向
<
- 標準輸出重定向
>
- 標準錯誤重定向
2>
假設我們有一個程序counter從鍵盤接受輸入字符,并返回輸入字符的個數.那么我們只需如下就可以讓counter從文件input.txt中輸入字符,并把結果存到res.txt中:
counter <input.txt >res.txt
cout進行輸出
ios_basic
類中存儲了描述格式狀態的信息,如計數系統,字段寬度,小數位數等,并提供了控制符來控制顯示整數時的計數系統,由于ios_basic
類是ostream
類的間接基類,所以可在ostream
類的對象中直接使用.例如,要控制整數以十進制,十六進制,八進制顯示可分別使用dec,hex,oct
控制符.
hex(cout); // 控制符不是成員函數,不必通過對象調用
進行如上設置后,cout將以十六進制顯示,直到將格式設置為其他選項為止.因為ostream
重載了<<
,所以一般以cout<<hex;
方式單獨使用控制符.
ps: 對于cin,以上控制符也是可用的,表示以什么進制解釋輸入的整數
字段寬度
使用成員函數width()
可實現將長度不同的數字放到寬度相同的字段中.原型為:
int width(); // 返回字段寬度的當前設置
int width(int i); // 將字段寬度設置為i并返回設置之前的字段寬度
注意: width()函數只影響接下來的一個項目,之后字段將恢復到默認值
填充字符
默認的cout使用空格填充字段中未被使用的部分,可使用成員函數fill(char)
可改變填充字符.與字段寬度設置不同的是它會一直有效直到更改為止.
設置浮點數顯示精度
已知c++默認精度是6位,但末尾的0不顯示.可以使用成員函數precision(int)
來設置顯示的精度,也就是數據的總位數.與fill()
類似,一旦設置就一直有效直到被更改.
有時輸出需要顯示末尾的0和小數點,ostream
類本身沒有提供這方面的函數,它的基類ios_basic
類提供的成員函數setf()
可以實現控制多種格式化特性,
使用setf()
函數需要進行很多設置,用到很多的ios_basic
的類級靜態常量,不是很友好.c++在頭文件iomanip
中提供了其他一些控制符可以更方面的實現格式化特性.如下給出三個最常用的控制符:
- setpricision(int): 設置精度
- setfill(char): 設置填充字符
- setw(int): 設置字段寬度
舉例如下:
#include<cmath>
#include<iomanip>
#include<iostream>
int main()
{
std::cout << std::fixed << std::right; // 顯示m末尾的0; 右對齊
std::cout << std::setprecision(4); // 設置顯示精度,一直有效
std::cout << std::setw(6) << "N" << std::setw(15) << "squre root" << std::setw(15) << "fourth root" << std::endl;
for (int i=10; i<=100; i+=10)
{
// 設置字段寬度(只對下一條輸出內容有效)和填充字符(一直有效)
std::cout << std::setw(4) << std::setfill('.') << i
<< std::setw(15) << std::setfill(' ') << sqrt(i) << std::setw(15) << sqrt(sqrt(i)) << std::endl;
}
return 0;
}
//下面為輸出
N squre root fourth root
..10 3.1623 1.7783
..20 4.4721 2.1147
..30 5.4772 2.3403
..40 6.3246 2.5149
..50 7.0711 2.6591
..60 7.7460 2.7832
..70 8.3666 2.8925
..80 8.9443 2.9907
..90 9.4868 3.0801
.100 10.0000 3.1623
cin進行輸入
cin
可自動識別輸入的數據類型,也就是說,它讀取從非空白符開始到與目標類型不匹配的第一個字符之間的所有內容.剩下不匹配的內容(如果還有的話)將留在輸入流中等待下一個cin
語句讀取.如果輸入與預期不匹配時,那么cin操作的對象將不會被改變并返回狀態0.可用來檢查輸入是否合法.
單字符輸入
- get(char&)和get() 提供不跳過空白符的單字符輸入功能
- get(char,int,char)和getline(char,int,char)在默認情況下讀取整行而不是一個單詞.
在有參數和沒參數的情況下,get()
方法都讀取下一個輸入字符,即使是不可見字符.get(char&)將輸入的字符賦給其參數并返回istream
對像(意味著可拼接,如cin.get(c1).get(c2)),get(void)將輸入的字符轉換為整形再返回.如下是兩種方式讀取到文件末尾的寫法:
char ch;
while(cin.get(ch))
{
// process...
}
int ch;
while((ch=cin.get())!=EOF)
{
// process...
}
字符串輸入
成員函數getline(),get()
都有讀取字符串的版本,他們的特征標都相同:
- istream& get(char*,int,char);
- istream& get(char*,int);
- istream& getline(char*,int,char);
- istream& getline(char*,int);
- istream& ignore(int,char);//int表示讀取最大字符數,char表示分界符
第一次參數表示輸入字符串的內存單元地址,第二個參數表示讀取的最大字符數(通常要加1用于存儲字符串結尾字符),第三個參數表示用于分界的字符串,也就是用于指定停止輸入的字符.要是沒有第三個參數則默認用換行符用作分界符.
上面get和getline的功能都是一樣的,只有一點區別,get會將分解符留在輸入流中,而getline則讀取分解符并丟棄掉.
其中ignore(int,char)
用于讀取指定數目的字符或者直到分界符為止,并把讀取到的字符串丟棄.