c++进阶---IO类的详细介绍(一)
IO類
c++為了更好的處理不同的種類的IO的操作,IO庫中定義了龐大的類庫來處理不同種類的IO操作,該類庫組成如下圖所示:
首先,我們先了解一下這個龐大的IO庫各個類之間的關系。
ios是最基本的父類,其中istream類和ostream類都繼承了ios類。
iostream類通過多重繼承繼承了istream類和ostream類。
ifstream、istringstream兩個類都是繼承了istream類,oftream、ostringstream兩個類都繼承了ostream類。
OK,另外,我們要知道:
處理標準輸入輸出的類:istream(從流中讀取數據,即處理輸入)、ostream(向流中寫入數據,即處理輸出)、iostream(處理輸入和輸出),這些類都被定義在頭文件:iostream中。
處理文件的輸入和輸出的類:ifstream(從文件讀取數據)、ofstream(向文件寫入數據)、fstream(讀寫文件),這些類都被定義在頭文件:fstream中
處理string流的類:istringstream(從string中讀取數據)、ostringstream(向string寫入數據)、stringstream(讀寫string)。然后,這些類都是定義在頭文件:sstream中。
另外,我們再補充一下標準輸入輸出中的一些知識:
cin是STL中定義的一個istream對象,它的作用是用于輸入。
cout、cerr、clog是STL中定義的一個ostream對象,它們的作用是用于輸出,其中cout是標準輸出,cerr是用于輸出錯誤或者警告信息,clog是用于輸出程序的一般性信息。其中cerr不經過緩沖區,直接把錯誤信息輸出到顯示器中,clog則先把信息放在緩沖區中。如果我們輸入一個endl,那么它將會把我們輸出緩沖區的內容全部輸出,并且輸出一個換行。
cin的詳解
程序的每次輸入都會建立一個輸入緩沖區。而我們的cin就是直接從這個輸入緩沖區獲取數據的,如果我們的緩沖區中有數據殘留(不為空時),那么cin對象直接從緩沖區讀取數據而不是請求輸入。另外,之前我們已經說過了cin是istream定義的一個對象,所以我們每次使用cin進行請求輸入的時候,都是在調用istream這個對象的方法,其中istream這個對象處理輸入的方法三種:cin>> 、cin.get() 、cin.getline().另外,cin是可以連續從緩沖區中讀取想要的數據的,它是以(tab 、space、enter)中的一種作為分隔符,
cin>>方法的介紹
注意事項:
(1)cin>>,其實是調用類的:istream & operator >>方法,它很多種重載的版本,分別用于處理字符、浮點數、整型等數據類型的輸入。
(2)cin>>,這個方法遇到了分割符(tab 、space、enter)時,就返回結束該次cin>>方法的調用。然后,如果我們調用cin>>方法時,從緩沖區讀取數據,如果開頭的數據是分隔符,那么直接把分割符忽略并且清除,直至第一個字符不是分隔符時才開始輸入。
示例展示:
#include<iostream>
#include<string>
using namespace std;
int main()
{
? ? int num;
? ? string s;
? ? cin >> num >> s;
? ? cout << num << " " << s << endl;
? ? getline(cin,s);
? ? cout << "string:" << s << endl;
? ? cout << endl;
? ? cout << "test" << endl;
? ? cout << endl;
? ? return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
輸入:
[回車][回車][回車]12 abcd[回車]
輸出:?
ps:首先了getline函數是不會忽略開頭的分割符的,我們開始保存到緩沖區的三個回車都被cin>>方法忽略了,而最后一個回車也在緩沖區中,但是沒有被忽略,而是被geiline函數讀了進來,所以在輸出test之前會有兩個換行。
cin.get()方法介紹
注意事項:
(1)cin.get()它有四種比較常見的重載格式:
int cin.get(); ? ?//不帶參數時,返回值為整型,但遇到了文件的結束,返回EOF,其實就是:-1
istream & cin.get(char & s) //返回值為輸入流。
istream & cin.get(char * s,streamsize n) //用于輸入一個字符串,但是它只接受參數時c語言風格字符串風格的參數,即是不接受string類的參數,而且它默認是以換行作為結束符。
istream & cin.get(char * s,streamsize n,char end) //用于輸入一個字符串,同樣參數必須是c風格的,當時它是以:字符end,作為結束符。
1
2
3
4
5
(2)上面的streamsize在頭文件iostream中的定義為long long型的數據類型的別名。所有版本的cin.get()方法是不會忽略開頭的分隔符符號的,而且它和cin>>一樣,遇到分隔符(tab、space 、enter)時,就結束該次方法的調用。最后的那個分隔符還是保存在緩沖區中。
示例展示:
#include<iostream>
using namespace std;
int main()
{
? ? char s;
? ? char s1;
? ? s1 = cin.get();
? ? cin.get(s);
? ? cout << (int)s1 << " " << (int)s << endl;?
? ? char str[5] = {NULL};
? ? char end;
? ? cin.get(str,5);
? ? cin.get(end);
? ? cout << str << " " << (int)end << endl;
? ? cout << endl;
? ? return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
輸入:
[回車][回車]test[回車]
輸出:
ok,我們從結果可以看出,我們開始輸入的兩個回車都被作為s1和s的初始值了,而我們字符串的最后一個回車也被end作為初始值了,從而可以看出cin.get()是不會忽略緩沖區中的分隔符的。
cin.getline()方法的介紹
注意事項:
(1)cin.getline()方法重要的幾個重載版本
//這里我cin.get后面的兩個方法一樣,用于輸入字符串。
//cin.getline不會將最后一個輸入的結束符或者換行符殘留在輸入緩沖區中,
//而是一起輸出到顯示器中。默認遇到‘\n’結束輸入。
istream & cin.getline(char *s ,streamsize n)?
//這個方法就是結束方式不同,它是遇到了字符:end就結束輸入。然后,輸出長度為n-1范圍內,end之前的所有字符。
istream & cin.getline(char * s,streamsize n,char end)?
1
2
3
4
5
6
7
8
代碼示例:
#include<iostream>
#include<string>
using namespace std;
int main()
{
? ? char s[5];
? ? char t;
? ? cin.getline(s, 5);
? ? cout << s << endl;;
? ? cin >> t;
? ? cout << t << endl;
? ? cout << endl;
? ? return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
輸入:
new[回車]f
輸出:
ok,從輸出的結果,我們可以發現,cin.getline是把我們輸入緩沖區的那個回車一并輸出來了。
補充:getline函數的介紹(用于輸入一行時使用,接受空格的輸入)
(1):C++中定義了一個在std名字空間的全局函數getline,因為這個getline函數的參數使用了string字符串,所以聲明在了string頭文件中了。
getline利用cin可以從標準輸入設備鍵盤讀取一行,當遇到如下三種情況會結束讀操作:1)到文件結束,2)遇到函數的定界符,3)輸入達到最大限度。
函數原型有兩個重載形式:
istream& getline ( istream& is, string& str);//默認以換行符結束
istream& getline ( istream& is, string& str, char end);//以end字符結束
1
2
3
(2)注意,getline遇到結束符時,會將結束符一并讀入指定的string中,再將結束符替換為空字符。因此,進行從鍵盤讀取一行字符時,建議使用getline,較為安全。但是,最好還是要進行標準輸入的安全檢查,提高程序容錯能力。另外,cin.getline()類似,但是cin.getline()屬于istream流,而getline()屬于string流,是不一樣的兩個函數。
代碼示例:
#include<iostream>
#include<string>
using namespace std;
int main()
{
? ? string str;
? ? getline(cin, str);
? ? cout << str;
? ? cout << endl;
? ? return 0;
}
1
2
3
4
5
6
7
8
9
10
11
輸入:
i am a student;
輸出:
gets_s 函數的介紹(一般不要用它好了,c中定義的語法),原來是gets,但是在新的vs2015中已經沒有gets了。只有gets_s
gets是C中的庫函數,在< stdio.h>申明,從標準輸入設備讀字符串,可以無限讀取,不會判斷上限,以回車結束或者EOF時停止讀取,所以程序員應該確保buffer的空間足夠大,以便在執行讀操作時不發生溢出。
函數原型:char *gets_s( char *buffer );
代碼示例:
#include<iostream>
using namespace std;
int main()
{
? ? char array[20] = { NULL };
? ? gets_s(array);
? ? cout << array << endl;
? ? return 0;
}
1
2
3
4
5
6
7
8
9
輸入:
new find
輸出:
重點內容補充:IO類的對象都是不可以拷貝的,所以如果我們需要使用IO類的對象作為函數的參數的時候或者返回值的時候,我們都要使用引用類型
條件狀態
因為我們在使用IO類的時候,總會出現一些錯誤,于是每個IO類都定義了一個iostate這個數據類型表示當前某個操作所處的狀態。其中IO庫定義了4個iostate類型的constexpr的值,它們分別是:
strm::badbit :首先,我們要說明strm代表的是IO類中的一種,例如istream、ostream、fstream等,babit表示系統級的錯誤,如果IO發生了不可修復的錯誤,badbit將被置位,而且流將無法再使用。
strm::failbit:這個會在流發生可修復的錯誤時,failbit將會被置位,例如:當我們本來是要求輸入一個數值的卻輸入了一個字符等錯誤,這種問題可以被修正的,流還可以繼續被使用。
strm::eofbit:當流達到了文件的結束的位置,eofbit和failbit這兩個狀態都會被置位。
strm::goodbit:當goodbit的值為1時,表示流未發生錯誤。
最后就是,只要badbit、eofbit、failbit任意一個狀態被置位,那么一切以流狀態為條件的語句都是返回false。例如:while(cin>>word),這條語句,只要上述三個中,有一個被置位,循環就結束。
IO庫中還定義了一組函數,用于查詢當前某種狀態的值。具體函數如下:其中s為一個流的對象。
s.eof() // 若流s的eofbit置位,則返回true
s.fail() // 若流s的failbit或badbit置位,則返回true
s.bad() // 若流s的badbit被置位,則返回true
s.good() // 若流s處于有效狀態,則返回true
1
2
3
4
在實際我們在循環中判斷流的狀態是否有效時,都直接使用流對象本身,比如:while(cin>>variable){cout<
#include<iostream>
using namespace std;
int main()
{
? ? int t;
? ? while (cin>>t)
? ? {
? ? ? ? cout << "cin.good的值:"<<cin.good() << endl;
? ? ? ? cout << "cin.eof的值:" << cin.eof() << endl;
? ? ? ? cout << "cin.fail的值:" << cin.fail() << endl;
? ? ? ? cout << "cin.bad的值:" << cin.bad() << endl;
? ? }
? ? cout << "結束后" << endl;
? ? cout << "cin.good的值:" << cin.good() << endl;
? ? cout << "cin.eof的值:" << cin.eof() << endl;
? ? cout << "cin.fail的值:" << cin.fail() << endl;
? ? cout << "cin.bad的值:" << cin.bad() << endl;
? ? system("pause");
? ? return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
輸入:1[空格]2[空格]d[回車]
輸出:
IO類庫還提供了3個函數來管理和設置流的狀態:
s.clear(); // 將流s中所有條件狀態復位,將流的狀態設置為有效,調用good會返回true
s.clear(flags); // 根據給定的flags標志位,將流s中對應的條件狀態復位,flags的類型為iostate
s.setstate(flags); // 根據給定的flags標志位,將流s中對應的條件狀態置位。,flags的類型為iostate
s.rdstate(); // 返回一個iostate類型的值,對應流當前的狀態。
1
2
3
4
注釋:Windows下標準輸入輸入文件結束符為Ctrl+z,Linux為Ctrl+d。
最后就是,我們來完成一下《c++ primer 第五版》的281那個練習題,代碼如下:
#include<iostream>
#include<string>
using namespace std;
istream & test(istream & s)
{
? ? string str;
? ? while ((s >> str).eof() == false)
? ? {
? ? ? ? cout << str << " " << endl;
? ? }
? ? cout << "結束" << endl;
? ? s.clear();
? ? return s;
}
int main()
{
? ? //Windows下標準輸入輸入文件結束符為Ctrl+z,Linux為Ctrl+d。
? ? test(cin);
? ? system("pause");
? ? return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
測試結果:
緩沖區詳解
(1)輸入緩沖區
觀察如下代碼:
#include<iostream>
using namespace std;
int main()
{
? ? char ch;
? ? while (cin >> ch)
? ? {
? ? ? ? cout << ch;
? ? }
? ? system("pause");
? ? return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
我們預想的是,我們輸入一個字符就顯示一個字符,但是實際上情況是如下圖:
其實,輸入字符立即回顯是非緩沖或直接輸入的一個形式,它表示你所鍵入的字符對正在等待的程序立即變為可用。相反,延遲回顯是緩沖輸入的例子,這種情況下你所鍵入的字符塊被收集并存儲在一個被稱為緩沖區的臨時存儲區域中。按下回車鍵可使你輸入的字符段對程序起作用。
緩沖輸入一般常用在文本程序內,當你輸入有錯誤時,就可以使用你的鍵盤更正修正錯誤。當最終按下回車鍵時,你就可以發送正確的輸入。
而在一些交互性的游戲里需要非緩沖輸入,如:游戲里你按下一個鍵時就要執行某個命令。
輸入緩沖分類:
完全緩沖:緩沖區被充滿時被清空(內容發送到其目的地)。這種類型的緩沖通常出現在文件輸入中。
行緩沖:遇到一個換行字符時被清空緩沖區。鍵盤的輸入是標準的行緩沖,因此按下回車鍵將清空緩沖區
(2)輸出緩沖區
每個輸出流都會管理一個緩沖區,用了保存程序讀寫的數據,這個和輸入緩沖區是一個道理的,但是輸出不一樣,我們是希望在程序結束是,輸出緩沖區中的內容都要被輸出去,所以會有緩沖區刷新,在下面這幾種情況會引起緩沖區的刷新(注意:如要程序異常終止,輸出緩沖區是不會被刷新的。當一個程序崩潰后,它所輸出的數據很可能停留在輸出緩沖區中等待打印。所以最好在每個輸出后加一個緩沖區刷新的操作):
程序正常結束,作為main函數的return操作的一部分,緩沖刷新被執行。
緩沖區滿時,需要刷新緩沖,而后新的數據才能繼續寫入緩沖區。
我們可以使用操縱符endl來顯式刷新緩沖區。
在每個輸出之后,我們可以用操縱符unitbuf設置流的內部狀態,來清空緩沖區。默認情況下,對cerr是設置unitbuf的,因此寫到cerr的內容都是立即刷新的。
一個輸出流被關聯到另一個流。在這種情況下,當讀寫被關聯的流時,關聯到的流的緩沖區會被刷新,cin和cerr都關聯到cout。因此讀cin或寫cerr會導致cout的緩沖區被刷新(cin立即回顯的一個原因)。
IO庫中除了endl可以刷新緩沖區外,ends和flush也會刷新緩沖區。只是它們會有一點差別:
cout << "hi!" << endl; // 輸出 hi 和一個換行符,然后刷新緩沖區?
cout << "hi!" << flush; // 輸出hi,然后刷新緩沖區,不附加任何額外字符?
cout << "hi!" << ends; // 輸出hi和一個空字符。然后刷新緩沖區
1
2
3
unitbuf操作符,如果我們希望每次輸出操作后都要刷新緩沖區,那么就可以使用:unitbuf,它就是告訴系統,以后每次進行輸出操作后都要指向flush操作,我們之前提到的cerr就是設置了unitbuf的
cout << unitbuf; // 所有輸出操作后都立即刷新緩沖區?
// 任何輸出都立即刷新,無緩沖?
cout << nounitbuf; // 回到正常的緩沖方式
1
2
3
4
點贊 4
————————————————
版權聲明:本文為CSDN博主「Ouyang_Lianjun」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_35644234/article/details/56679127
總結
以上是生活随笔為你收集整理的c++进阶---IO类的详细介绍(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sizeof获取类型的位
- 下一篇: C++中流状态badbit, failb