C++學習-入門到精通【11】輸入/輸出流的深入剖析
目錄
- C++學習-入門到精通【11】輸入/輸出流的深入剖析
- 一、流
- 1.傳統流和標準流
- 2.iostream庫的頭文件
- 3.輸入/輸出流的類的對象
- 二、輸出流
- 1.char* 變量的輸出
- 2.使用成員函數put進行字符輸出
- 三、輸入流
- 1.get和getline成員函數
- 2.istream的成員函數peek、putback和ignore
- 3.類型安全
- 四、使用read、write和gcount的非格式化的I/O
- 五、流操縱符簡介
- 1.整數流的基數:dec、oct、hex和setbase
- 2.浮點精度
- 3.位寬(width和setw)
- 4.用戶自定義輸入流操縱符
- 六、流的格式狀態和流操縱符
- 1.尾數零和小數點(showpoint)
- 2.對齊(left、right和internal)
- 3.內容填充(fill和setfill)
- 4.整數流的基數
- 5.浮點數、科學計數法和定點小數記數法(scientific、fixed)
- 6.大寫/小寫控制(uppercase)
- 7.指定布爾類型(boolalpha)
- 8.通過成員函數flags設置和重置格式狀態
- 七、流的錯誤狀態
一、流
C++的I/O是以一連串的字節流的方式進行的。在輸入操作中,字節從設備(例如,鍵盤、磁盤驅動器、網絡連接等)流向內存。在輸出操作中,字節從內存流向設備(例如,顯示屏、打印機、磁盤驅動器、網絡連接等)。入:表示進入內存;出:表示離開內存。以內存為參照物。
這里的流是一個抽象的概念,表示一個連續的數據序列。對于上面的不同設備而言,它們處理的數據格式其實并不相同,所以我們使用一個流的抽象概念將數據的生產者和消費者解耦(生產者只需將生成的數據給到流,之后輸出到各種設備的不同操作由流來決定;反之亦然。)這就使得程序只需關注數據本身,不需要關注到數據如何流動到不同設備之中。
應用程序通過字節傳達信息。字節可以組成字符、原始數據、圖形圖像、數字語音、數字視頻或者任何應用程序所需要的其他信息。系統I/O結構應該能持續可靠地將字節從設備傳輸到內存,反之亦然。這種傳輸在設備中往往包含一些機械運動,例如,磁盤的旋轉,或鍵盤的敲擊。數據傳輸所花費的時間遠遠大于處理器內部處理數據所需要的時間。所以I/O操作需要仔細計劃和協調,以保證最優的性能。
C++中同時提供低層次的
和高層次的
I/O功能。
低層次的I/O功能(也就是非格式化的I/O)指定字節應從設備傳輸到內存還是從內存傳輸到設備。這種傳輸通過針對單個字節,并提供速度快、容量大的傳輸。但是對于程序員來說不方便。
程序員通常更喜歡高層次的I/O(也就是格式化的I/O)。因為在這種輸出方式中字節被組成了有意義的單元,例如整數、浮點數、字符、字符串或者用戶自定義類型。除了大容量文件處理之外,這種面向類型的方法能夠滿足絕大多數的I/O處理。
1.傳統流和標準流
C++的傳統流庫(<stdio.h>中的函數)允許輸入/輸出char類型的字符。一個char只占據一個字節的大小,它只能表示一個字符的有限集(例如,ASCII字符集)。然而,許多語言使用的字母表包含更多的符號,這是單字節的char無法表示的。
C++包含標準流庫(,之類的庫),可以描述這些新的字符。
2.iostream庫的頭文件
絕大多數C++程序都包含了<iostream>
頭文件,該頭文件中聲明了所有I/O操作所需的基礎服務。其中定義了cin、cout、cerr和clog
對象,分別對應于標準輸入流、標準輸出流、無緩沖的標準錯誤流和有緩沖的標準錯誤流。同時還提供了非格式化和格式化的I/O服務。
<iomanip>
頭文件中聲明了參數化流操縱符(例如,之前我們使用過的setw和setprecision)用于向格式化I/O提供有用的服務。
<fstream>
頭文件中聲明了文件處理服務。
3.輸入/輸出流的類的對象
iostream庫提供了許多模板來處理一般的I/O操作。例如,類模板basic_istream
支持輸入流操作,類模板basic_ostream
支持輸出流操作,類模板basic_iostream
同時支持輸入流和輸出流操作。
I/O流模板層次和運算符重載
模板basic_istream和basic_ostream派生自同一個基模板basic_ios
。模板basic_iostream則從模板basic_istream和basic_ostream多重繼承而來。下面是它們的UML類圖。
運算符重載為輸入和輸出操作提供了更加方便的符號。左移運算符<<
被重載用于實現流的輸出,被稱為流插入運算符
,右移運算符>>
被重載用于實現流的輸入,被稱為流提取運算符
。這些運算符通常與標準流對象cin、cout、cerr和clog
,以及用戶自定義的流對象一起使用。
標準流對象cin、cout、cerr和clog
預定義對象cin
是一個istream類的實例,并且被綁定到標準輸入設備(通常是鍵盤)。在下面的語句中,流提取運算符>>
用于使一個整形變量grade的值從cin輸入到內存。
cin >> grade;
注意,由編譯器確定grade的數據類型,并且選擇合適的流提取運算符重載。假設grade已經被正確的聲明,流提取運算符不需要附加任何類型信息(附加類型信息,就像C語言的I/O方式一樣,在函數中顯式的指定輸入數據的類型)。標準庫中重載的>>運算符可以用來輸入內置類型、字符串和指針的值。
預定義對象cout
是一個ostream類的實例,并且被綁定到標準輸出設備(通常是顯示器)。在下面的語句中,流插入運算符<<
用于將變量grade的值從內存中輸出到標準輸出設備:
cout << grade;
注意,這里也是由編譯器確定grade的數據類型,并且選擇合適的流插入運算符,因此流插入運算符也同樣不需要附加類型信息。
預定義對象cerr
和clog
都是ostream類的實例,且都被綁定到標準錯誤設備。對象cerr的輸出是無緩沖的。每個針對cerr的流插入的輸出必須立刻顯示,這對于迅速提示用戶發生錯誤非常合適。而對象clog是輸出是有緩沖的,每個針對clog的流插入的輸出先保存到緩沖區中,直到緩沖區被填滿或是被清空才會輸出。
文件處理模板
C++文件處理乃至類模板basic_ifstream
、basic_ofstream
和basic_fstream
。模板basic_ifstream繼承自basic_istream,basic_ofstream繼承自basic_ostream,而basic_fstream則繼承自basic_iostream。它們的UML類圖如下:
二、輸出流
ostream提供了格式化的和非格式化的輸出功能。輸出功能包括使用流插入運算符<<
執行標準數據類型的輸出;通過成員函數put
進行字符輸出;通過成員函數write
進行非格式化的輸出;十進制、八進制、十六進制格式的整數輸出;具有不同精確度的浮點數的輸出,或是具有強制小數點的浮點數的輸出;用指定符號填充數據域的輸出;以及使用科學計數法和十六進制符號表示的大寫字母的輸出。
1.char* 變量的輸出
C++能自動判定數據類型,這是它相對于C的一種改進。但是,這一特性有時候會產生一些問題。比如,現在我們要打印一個字符串的char*的值(該字符串首字符的地址)。然而,<<
運算符已被重載用于打印將char*數據類型作為以空字符結尾的字符串。解決的辦法就是將char*強制轉化為void*類型
(如果程序員想輸出一個地址,那么就都應該對指針變量進行這樣的轉換)。
下面就是分別以字符串形式和地址形式輸出char*的值的例子。
#include <iostream>
using namespace std;int main()
{const char* const word = "hehe";cout << "Value of word is " << word<< "\nValue of static_cast<const void*>(word) is " << static_cast<const void*>(word) << endl;
}
運行結果:
2.使用成員函數put進行字符輸出
可以使用成員函數put輸出字符,例如,語句:
cout.put('A');
就可以顯示單個字符A
。put也可以級聯使用(該成員函數返回的也是一個ostream對象的引用)。例如,
cout.put('A').put('B');
三、輸入流
格式化的和非格式化的輸入功能是由istream來提供的。流提取運算符通常跳過輸入流中的空白字符(例如,空格、制表符和換行符)
,我們也可以改變它的這種行為。
在每個輸入操作之后,流提取運算符給接收到所提取的信息的流對象返回一個引用。如果引用被用作判斷條件(例如,while(cin >> grade)
),那么將隱式調用流重載的void*強制轉換運算符函數,根據最后輸入操作的成功與否,將引用轉化為非空指針或是空指針值。 非空指針值轉化為bool值true,表示操作成功;空指針值則轉化成bool值false,表示操作失敗。
當試圖超過流的末尾進行讀取操作時,流重載的void*強制轉化運算符返回一個空指針,表示已經讀到文件的末尾。
每個流對象都包含一組狀態位來控制流的狀態(例如,格式化、設置錯誤狀態等),流重載的void*強制類型轉換運算符使用這些狀態位來決定是返回非空值還是空值。當輸入錯誤的數據類型時,流提取的failbit
狀態位被設置;當操作失敗時,流的badbit
位被設置。
1.get和getline成員函數
沒有實參的成員函數get從指定流中輸入一個字符(包括空白字符及其他非圖形字符,比如表示文件尾的鍵序列等),并將這個值作為函數調用的返回值返回。這個版本的get函數在遇到流中的文件尾時返回EOF
值。
使用成員函數eof、get和put
下面代碼展示了成員函數eof、get和put的使用方法。
#include <iostream>
using namespace std;int main()
{int character;cout << "Before input, cin.eof() is " << cin.eof() << endl<< "Enter a sentence followed by end-of-file:" << endl;while ((character = cin.get()) != EOF){cout.put(character);}cout << "\nEOF in this system is: " << character << endl;cout << "After input of EOF, cin.eof() is " << cin.eof() << endl;
}
注意Windows系統中的文件結束符是通過組合鍵ctrl + z
,UNIX系統中組合鍵ctrl + d
。
運行結果:
注意,只有當程序試圖越過流中的最后一個字符進行讀操作時,eof函數才返回true。也就是只有當文件指針指向結束符時,還要再將進行讀取操作,此時eof函數才會返回true。
比較cin和cin.get
下面程序對使用流提取cin進行輸入(讀取字符直到遇到空白字符)和cin.get進行輸入的不同之處。注意下面代碼中使用get并沒有指定分隔符,所以它使用默認的分隔字符(\n
)。
#include <iostream>
using namespace std;int main()
{const int SIZE = 80;char buffer1[SIZE];char buffer2[SIZE];cout << "Enter a sentence: " << endl;cin >> buffer1;cout << "\nThe string read with cin was:" << endl<< buffer1 << endl << endl;cin.get(buffer2, SIZE);cout << "The string read with cin.get was:" << endl<< buffer2 << endl << endl;
}
運行結果:
可以看到cin.get會遇到\n
之前的,小于讀取字符數的所有字符。而使用cin進行讀取則只會讀取到第一次遇到空白字符之前的字符。
成員函數getline
#include <iostream>
#include <string>
using namespace std;int main()
{string str1;string str2;cout << "Enter a sentence: " << endl;getline(cin, str1, ' ');cout << "\nThe string read with getline(cin, str1, ' ') was:" << endl<< str1 << endl << endl;getline(cin, str2);cout << "The string read with get(cin, str2) was:" << endl<< str2 << endl << endl;
}
運行結果:
可以看到使用getline獲取的第二個string對象中,首字符并不是空格(被丟棄了)。
2.istream的成員函數peek、putback和ignore
putback這個成員函數對于掃描輸入流,在其中搜索以特定字符開頭的字段的應用程序很有用。
例如,此時我們要輸出一個以字母’a’開頭的單詞,所以我們需要從流讀取一個單詞的首字符判斷是否符合要求,在讀取到一個單詞的首字符為’a’時,我們要將這個單詞保存到一個變量中,但是因為我們已經從流中讀取了它的第一個字符,我們現在需要將這個字符’a’放回到流的最前面,使得我們可以從流中再次讀取到這個完整的單詞。
該函數也可以實現像上面的putback函數一樣的預讀
操作。只是它壓根沒有把字符提取出來,所以也就不需要放回。
3.類型安全
C++提供類型安全的I/O。重載<<
和>>
運算符可接收各種指定類型的數據項,如果遇到意料之外的數據類型,各種相應的錯誤位就會被設置,用戶可以通過檢測錯誤位來判斷I/O操作是否成功。如果沒有為用戶自定義類型重載運算符<<和>>,并且試圖輸入或輸出一個該用戶自定義類型的對象的內容,那么編譯器就會報錯。
四、使用read、write和gcount的非格式化的I/O
下面給出一個使用read、write和gcount的例子。
#include <iostream>
using namespace std;#define SIZE 80int main()
{char buffer[SIZE];cout << "Enter a sentence:" << endl;cin.read(buffer, 20); // 從輸入流提取20個字符存入buffer中cout << "The sentence entered was: " << endl;cout.write(buffer, cin.gcount()); // 之前提取了多少個字符,就輸出多個字符cout << endl;
}
運行結果:
五、流操縱符簡介
C++提供多種流操縱符來完成格式化的任務。流操縱符的功能包括設置域的寬度、設置精確度、設置和取消格式狀態、設置域的填充字符、刷新流、向輸出流添加新行(并刷新流)、在輸出流中添加一個新字符、跳過輸入流中的空白,等等。
1.整數流的基數:dec、oct、hex和setbase
為了能夠更改流中整數的基數(默認的基數是10,也就是十進制),使之不局限于默認的基數,可以插入hex操縱符基數設置為十六進制,或者插入oct操縱符將基數設置為八進制。插入dec操縱符將整型流的基數重新設置為十進制。
當然除了使用上面提到的流操縱符,還可以使用setbase這個流操縱符來設置基數,該操縱符通過一個整數參數10、8、或16將基數分別設為十進制、八進制或十六進制。由于setbase有一個參數,所以也稱為參數化流操縱符
。而要使用任何參數化的操縱符就必須包含<iomanip>
這個頭文件。流的基數值只有被顯式更改才會發生變化,所以上面提到這些流操縱符的設置都是“黏性”的。
下面給出這幾個流操縱符的使用示例:
#include <iostream>
#include <iomanip>
using namespace std;int main()
{int number;cout << "Enter a decimal number: ";cin >> number;cout << number << " in hexadecimal is " << hex << number << endl;cout << dec << number << " in octal is " << oct << number << endl;cout << setbase(10) << number << " in setbase(7) is " << setbase(7) << number << endl<< "in setbase(10) is " << setbase(10) << number << endl;
}
運行結果:
從結果中可以看出setbase流操縱符只能設置為10、8或16這三種基數。
2.浮點精度
可以使用流操縱符setprecision
或ios_base
的成員函數precision
來控制浮點數的精度(也就是小數點右邊的位數)。這兩種操作的設置都是黏性的。調用無參數的成員函數precision
將返回當前的精度設置。
下面給出一個使用示例:
#include <iostream>
#include <iomanip>
using namespace std;int main()
{double d = 3.14159265359;cout << "The default precision is " << cout.precision() << "\n\n";cout << "With default precision d is " << d << "\n";cout.precision(8);cout << "With high precision d is " << d << "\n\n";cout << "Current precision is " << cout.precision() << "\n\n";cout << setprecision(6) << "With setprecision(6) d is " << d << "\n\n";cout << fixed;cout << "\nAfter setting fixed:\n\n";cout << "With cout.precision(6) d is ";cout.precision(6);cout << d << "\n\n";cout << "With setprecision(8) d is " << setprecision(8)<< d << "\n\n";
}
運行結果:
從結果中看,如果沒有使用流操縱符fixed
,成員函數percision
和參數化流操縱符setprecision
設置的整個浮點數的有效數位;而使用fixed
之后,它指定的就是小數點后面的有效位數。
3.位寬(width和setw)
成員函數width
(基類是ios_basic
)可以設置域寬(也就是輸出值所占的字符位數或是可輸入的最大字符數)并且返回原先的位寬。
如果輸出值的寬度比域寬小,則插入填充字符進行填充。寬度大于指定寬度的值不會被截短
,會將整個值都打印出來。
提示
寬度設置只適用于下一次輸入或輸出(非黏性),之后的寬度被隱式地設置為0(默認設置)。
使用示例:
#include <iostream>
using namespace std;int main()
{int widthValue = 3;char sentence[10];cout << "Enter a sentence:" << endl;cin.width(5); // 最多輸入5個字符while (cin >> sentence){cout.width(widthValue++);cout << sentence << endl;cin.width(5);}
}
運行結果:
注意,使用cin讀取字符時,會在遇到空白字符時停下,并跳過空白字符,下一次讀取直接從下一個非空白字符開始讀取。
使用width
成員函數,會讀取(參數 - 1)個字符,因為需要在每次讀取的字符串后面添加一個\0
。
所以才會出現上面的輸出。
4.用戶自定義輸入流操縱符
程序員可以創建自己的流操縱符。下面的代碼中展示了創建和使用新的無參數操縱符bell
、carriageReturn
、tab
和endLine
。對于輸出流操縱符來說,它們的返回類型都必須是ostream&
類型的。
#include <iostream>
using namespace std;// 輸出一個\a
ostream& bell(ostream& output)
{return output << '\a';
}// 輸出一個 \r
ostream& carriageReturn(ostream& output)
{return output << '\r';
}// 輸出一個 \t
ostream& tab(ostream& output)
{return output << '\t';
}// 輸出一個 endl
ostream& endLine(ostream& output)
{return output << '\n' << flush;
}int main()
{cout << "Testing the tab manipulator:" << endLine<< 'a' << tab << 'b' << tab << 'c' << endLine;cout << "Testing the carriageReturn and bell manipulator:" << endLine<< ".....................";cout << bell;cout << carriageReturn << "---------" << endLine;
}
運行結果:
六、流的格式狀態和流操縱符
在I/O流操作中,可以使用多種流操縱符來指定各種格式。流操縱符可以控制輸出格式。
下表中列出了類ios_base中可能會使用到的流操縱符。
流操縱符 | 描述 |
---|---|
skipws | 跳過輸出流的空白字符。使用流操縱符noskipws來重置設置 |
left | 域的輸出左對齊。必要時在右邊填充字符 |
right | 域的輸出右對齊。必要時在左邊填充字符 |
internal | 表示域中的數字的符號左對齊,同時域的數字的數值部分右對齊(中間使用填充字符填充) |
boolalpha | 指定bool類型的值以true或false的形式顯示。noboolalpha指定bool類型的值以1或0的形式顯示 |
dec | 整數以十進制數顯示 |
oct | 整數以八進制數顯示 |
hex | 整數以十六進制數顯示 |
showbase | 指明在數字的前面顯示該數的基數(以0開頭表示八進制,0x表示十六進制)。使用流操縱符noshowbase可以取消數字前的基數顯示 |
showpoint | 指明浮點數必須顯示小數點。通常使用fixed流操縱符來確保小數點右邊數字的位數,即使全部為0。可以使用流操縱符noshowpoint重置該設置 |
uppercase | 指明當顯示十六進制數時使用大寫字母,并且在科學計數法表示浮點數時使用大寫字母E。可以使用流操縱符nouppercase來重置該設置 |
showpos | 在正數(負數本來就會顯示符號)前顯示加號(+)。可以使用流操縱符noshowpos來重置該設置 |
scientific | 以科學計數法顯示輸出浮點數 |
fixed | 以定點小數形式顯示浮點數,并指定小數點右邊的位數 |
1.尾數零和小數點(showpoint)
流操縱符showpoint
強制要求浮點數的輸出必須帶小數點和尾數零。比如說浮點數79.0,在不使用showpoint時顯示79,使用showpoint時則顯示79.0000(尾數零取決于當前的精確度)。要重置showpoint的設定,需要使用流操縱符noshowpoint
。
當不使用fixed
或scientific
時,精確度表示顯示的有效位數,而不是小數點后的數字的位數。
#include <iostream>
using namespace std;int main()
{cout << "Before using showpoint:" << '\n';cout << "6.6600 prints as: " << 6.6600 << endl<< "6.6000 prints as: " << 6.6000 << endl<< "6.0000 prints as: " << 6.0000 << "\n\n";cout << "After using showpoint:" << '\n';cout << showpoint << "6.6600 prints as: " << 6.6600 << endl<< "6.6000 prints as: " << 6.6000 << endl<< "6.0000 prints as: " << 6.0000 << "\n\n";
}
運行結果:
2.對齊(left、right和internal)
直接看下面的例子
#include <iostream>
#include <iomanip>
using namespace std;int main()
{int x = 12345;cout << "Default is right justified:" << endl<< setw(10) << x;cout << "\n\nUse std::left to left justify x:\n"<< left << setw(10) << x;cout << "\n\nUse std::internal to internal justify x:\n"<< showpos << internal << setw(10) << x;cout << "\n\nUse '*' to fill space x:\n"<< setfill('*') << setw(10) << x << endl;}
運行結果:
其中在使用internal流操縱符時,還輔以showpos和setfill這兩個流操縱符,其中showpos是顯示在數字前顯示符號,setfill是設置在輸出內容比域寬小時進行填充的字符。
3.內容填充(fill和setfill)
成員函數fill
指定對齊域的填充字符。如果沒有字符被指定,則默認使用空格符填充。fill函數返回設定之前的填充字符。setfill流操縱符也用于設置填充字符。
可以參照上一個示例代碼。
其中成員函數fill
的使用,只需要在其前面使用一個ostream對象即可,例如:cout.fill('*');
4.整數流的基數
使用流操縱符showbase
可以要求整數的基數被輸出。
例如:
#include <iostream>
using namespace std;int main()
{int x = 100;cout << "Printing integers preceded by their base:" << endl<< showbase;cout << x << endl;cout << oct << x << endl;cout << hex << x << endl;
}
運行結果:
5.浮點數、科學計數法和定點小數記數法(scientific、fixed)
“黏性”流操縱符scientific
和fixed
可以控制浮點數的輸出格式。流操縱符scientific
要求浮點數以科學計數法的格式輸出。流操縱符fixed
要求浮點數以指定小數位數的形式顯示(可以使用成員函數precision或流操縱符setprecision指定小數位數)。如果不使用其他操縱符,由浮點數的值決定浮點數的輸出格式。
使用示例:
#include <iostream>
using namespace std;int main()
{double x = 0.001234567;double y = 1.946e9;cout << "Displayed in default format:\n"<< x << '\t' << y << endl;cout << "\nDisplayed in scientific format:\n"<< scientific << x << '\t' << y << endl;cout << "\nDisplayed in fixed format:\n"<< fixed << x << '\t' << y << endl;
}
運行結果:
6.大寫/小寫控制(uppercase)
流操縱符uppercase
在輸出十六進制整數基數和科學計數格式的浮點數時,分別輸出大寫字母X和E。還可以使十六進制整數中的字母都以大寫字母形式顯式。在默認情況下,十六進制和科學計數格式的浮點數中的字母都以小寫字母顯示。如果要取消uppercase的設置,只需要輸出流操縱符nouppercase
即可。
使用示例:
#include <iostream>
using namespace std;int main()
{cout << "Printing uppercase letters in scientific" << endl<< "notation exponents and hexadecimal values:" << endl;cout << uppercase << 4.28e10 << endl<< hex << showbase << 123456789 << endl;
}
運行結果:
7.指定布爾類型(boolalpha)
boolalpha
和noboolalpha
的設置為“黏性”的。直接看下面的示例:
#include <iostream>
using namespace std;int main()
{bool booleanValue = true;cout << "booleanValue is " << booleanValue << endl;cout << "booleanValue (after using boolalpha) is "<< boolalpha << booleanValue << "\n\n";cout << "\nswitch booleanValue and use noboolalpha" << endl;booleanValue = false;cout << noboolalpha << endl;cout << "booleanValue is " << booleanValue << endl;cout << "booleanValue (after using boolalpha) is " << boolalpha << booleanValue << endl;
}
運行結果:
8.通過成員函數flags設置和重置格式狀態
通過上面的學習,我們已經知道了如何使用流操縱符來更改輸出格式,那么我們在使用了流操縱符設置了格式之后,要如何將輸出格式重置為默認狀態呢?
無參數的成員函數flags
將當前的格式設置以fmtflags
數據類型(ios_base類中的)的形式返回,它表示了格式狀態。
擁有一個fmtflags
參數的成員函數flags
將格式狀態設置為其參數指定的格式狀態,并返回之前的狀態設定。
使用示例:
#include <iostream>
using namespace std;int main()
{int integerValue = 1000;double doubleValue = 0.0947628;// 顯示當前的格式狀態cout << "The value of the flags variable is: " << cout.flags()<< "\nPrint int and double in original format:\n"<< integerValue << '\t' << doubleValue << "\n\n";ios_base::fmtflags originalFormat = cout.flags();cout << showbase << oct << scientific; // 改變格式cout << "The value of the flags variable is: " << cout.flags()<< "\nPrint int and double in new format:\n"<< integerValue << '\t' << doubleValue << "\n\n";cout.flags(originalFormat); // 重置格式狀態cout << "The restored value of the flags variable is: " << cout.flags()<< "\nPrint values in original format again:\n"<< integerValue << '\t' << doubleValue << "\n\n";
}
運行結果:
七、流的錯誤狀態
流的狀態可以通過檢測ios_base
類中的相應位來判斷。
注意,在絕大多數情況下,如果eofbit被設置了,那么failbit也會被設置。
#include <iostream>
using namespace std;int main()
{cout << "Before a bad input operation:"<< "\ncin.rdstate(): " << cin.rdstate()<< "\ncin.eof(): " << cin.eof()<< "\ncin.fail(): " << cin.fail()<< "\ncin.bad(): " << cin.bad()<< "\ncin.good(): " << cin.good() << "\n\n";int number;cout << "Expects an integer, but enter a character: ";cin >> number;cout << "\nAfter a bad input operation:"<< "\ncin.rdstate(): " << cin.rdstate()<< "\ncin.eof(): " << cin.eof()<< "\ncin.fail(): " << cin.fail()<< "\ncin.bad(): " << cin.bad()<< "\ncin.good(): " << cin.good() << "\n\n";cin.clear(); // 重置錯誤狀態,該函數的默認參數是std::ios_base::goodbitcout << "After cin.clear()" << "\ncin.rdstate(): " << cin.rdstate()<< "\ncin.eof(): " << cin.eof()<< "\ncin.fail(): " << cin.fail()<< "\ncin.bad(): " << cin.bad()<< "\ncin.good(): " << cin.good() << "\n\n";
}
運行結果:
當遇到文件尾時,輸入流有eofbit將被設置。當試圖越過流的末尾提取數據時,程序可以調用成員函數eof
來判斷是否遇到了文件尾。遇到文件尾返回true
,否則返回false
。
當在流中發生格式錯誤時,failbit位將被設置,并且不會讀入任何字符,例如,像上面的測試程序一樣,預期從流中接收一個整數,但是接收到一個字符,在遇到這種情況時,這些字符不會丟失。成員函數fail
將報告流操作是否失敗了。通常這種錯誤是可恢復的(數據沒有丟失)。
當發生數據丟失錯誤時,將會設置badbit
位。成員函數bad
將報告流操作是否失敗了。一般情況下,這種嚴重的錯誤是不可恢復的(數據已經丟失)。
如果流中的eofbit、failbit、badbit
都沒有被設置,那么goodbit
將設置。
成員函數rdstate
返回流的錯誤狀態。可以使用該函數的返回值來判斷錯誤狀態,例如,將該函數的返回值與不同的錯誤位進行"相與",結果為1的即是出現錯誤的狀態位。
成員函數clear
重置流的錯誤狀態,默認參數為goodbit
,既將流的狀態設為good
。
所以也可以使用下面的語句將流的狀態設為錯誤cout.clear(ios::failbit);