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