C++的輸入和輸出與標準輸出流

一、 C++ 輸入輸出的含義

   以前所用到的輸入和輸出,都是以終端爲對象的,即從鍵盤輸入數據,運行結果輸出到顯示器屏幕上。從操作系統的角度看,每一個與主機相連的輸入輸出設備都被看作一個文件。程序的輸入指的是從輸入文件將數據傳送給程序,程序的輸出指的是從程序將數據傳送給輸出文件。C++的輸入與輸出包括以下3方面的內容:
   1、對系統指定的標準設備的輸入和輸出。簡稱標準I/O。(設備)
   2、以外存磁盤(或光盤)文件爲對象進行輸入和輸出。簡稱文件I/0。(文件)
   3、對內存中指定的空間進行輸入和輸出。簡稱串I/O。(內存)

   C++採取不同的方法來實現以上3種輸人輸出。 爲了實現數據的有效流動,C++系統提供了龐大的I/O類庫,調用不同的類去實現不同的功能。

二、 C++的I/O對C的發展—類型安全和可擴展性

C語言中I/O存在問題:
   1、在C語言中,用prinff和scanf進行輸入輸出,往往不能保證所輸入輸出的數據是可靠的、安全的。學過C語言的讀者可以分析下面的用法:想用格式符%d輸出一個整數,但不小心錯用了它輸出單精度變量和字符串,會出現什麼情況?假定所用的系統int型佔兩個字節。
  1. printf("%d",i);   //i爲整型變量,正確,輸出i的值  
  2. printf("%d",f);   //f爲單精度變量,輸出變量中前兩個字節的內容  
  3. printf("%d","C++");//輸出字符串"C++”的起始地址  

   編譯系統認爲以上語句都是合法的,而不對數據類型的合法性進行檢查,顯然所得到的結果不是人們所期望的。

   2、在用scanf輸入時,有時出現的問題是很隱蔽的。如
  1. scanf("%d",&i); //正確,輸入一個整數,賦給整型變量i  
  2. scanf("%d",i);   //漏寫&  
   假如已有聲明語句"int i=1",定義i爲整型變量,其初值爲1。編譯系統不認爲上面的scanf語句出錯,而是將輸入的值存放到地址爲000001的內存單元中,這個錯誤可能產生嚴重的後果。

注意:C++爲了與C兼容,保留了用printf和scanf進行輸出和輸入的方法,以便使過去所編寫的大量的C程序仍然可以在C++的環境下運行,但是希望讀者在編寫新的C++程序時不要用C的輸入輸出機制,而要用C++自己特有的輸入輸出方法。在C++的輸入輸出中,編譯系統對數據類型進行嚴格的檢查,凡是類型不正確的數據都不可能通過編譯。因此C++的I/0操作是類型安全(typesafe)的。

   3、用printf和scanf可以輸出和輸入標準類型(如:int,float,double,char)的數據,但無法輸出用戶自己聲明的類型(如數組、結構體、類)的數據。在C++中,會經常遇到對類對象的輸入輸出,顯然無法使用printf和scanf來處理。C++的I/O操作是可擴展的,不僅可以用來輸入輸出標準類型的數據,也可以用於用戶自定義類型的數據。C++對標準類型的數據和對用戶聲明類型數據的輸入輸出,採用同樣的方法處理。顯然,在用戶聲明瞭一個新類後,是無法用printf和scanf函數直接輸出和輸入這個類的對象的。

解決辦法:
   可擴展性是C++輸入輸出的重要特點之一,它能提高軟件的重用性,加快軟件的開發過程。
   C++通過I/O類庫來實現豐富的I/0功能。這樣使C++的輸入輸出明顯地優於C語言中的pfintf和scanf,但是也爲之付出了代價,C++的I/O系統變得比較複雜,要掌握許多細節。在本章中只能介紹其基本的概念和基本的操作,有些具體的細節可在日後實際深入應用時再進一步掌握。

三、 C++的輸入輸出流

   輸入和輸出是數據傳送的過程,數據如流水一樣從一處流向另一處。C++形象地將此過程稱爲流(stream)。C++的輸入輸出流是指由若干字節組成的字節序列,這些字節中的數據按順序從一個對象傳送到另一對象。流表示了信息從源到目的端的流動。在輸入操作時,字節流從輸入設備(如鍵盤、磁盤)流向內存,在輸出操作時,字節流從內存流向輸出設備(如屏幕、打印機、磁盤等)。流中的內容可以是ASCII字符、二進制形式的數據、圖形圖像、數字音頻視頻或其他形式的信息。

   實際上,在內存中爲每一個數據流開闢一個內存緩衝區,用來存放流中的數據。當用cout和插入運算符“<<”向顯示器輸出數據時,先將這些數據送到程序中的輸出緩衝區保存,直到緩衝區滿了或遇到endl,就將緩衝區中的全部數據送到顯示器顯示出來。在輸入時,從鍵盤輸入的數據先放在鍵盤緩衝區中,當按回車鍵時,鍵盤緩衝區中的數據輸入到程序中的輸入緩衝區,形成cin流,然後用提取運算符“>>”從輸入緩衝區中提取數據送給程序中的有關變量。總之,流是與內存緩衝區相對應的,或者說,緩衝區中的數據就是流。

   在C++中,輸入輸出流被定義爲類。C++的I/0庫中的類稱爲流類(streamclass)。用流類定義的對象稱爲流對象。

   前面曾多次說明,cout和cin並不是C++語言中提供的語句,它們是iostream類的對象,在未學習類和對象時,在不致引起誤解的前提下,爲敘述方便,把它們稱爲cout語句和cin語句。正如C++並未提供賦值語句,只提供賦值表達式,在賦值表達式後面加分號就成了C++的語句,爲方便起見,我們習慣稱之爲賦值語句。又如,在C語言中常用printf和scanf進行輸出和輸入,printf和scanf是C語言庫函數中的輸入輸出函數,一般也習慣地將由printf和scanf函數構成的語句稱爲printf語句和scanf語句。在使用它們時,對其本來的概念要有準確的理解。

1.iostream類庫中有關的類
   C++編譯系統提供了用於輸人輸出的iostream類庫。iostream這個單詞是由3個部分組成的,即i-o-stream,意爲輸入輸出流。在iostream類庫中包含許多用於輸入輸出的類。

   ios是抽象基類,由它派生出istream類和ostream類,兩個類名中第1個字母i和。分 別代表輸入(mput)和輸出(output)。istream類支持輸入操作,ostream類支持輸出操作, iostream類支持輸入輸出操作。iostream類是從istream類和ostream類通過多重繼承而派生的類。

   C++對文件的輸人輸出需要用ifstream和ofstream類,兩個類名中第1個字母i和o分別代表輸入和輸出,第2個字母f代表文件(file)。ifstream支持對文件的輸入操作,ofstream支持對文件的輸出操作。類ifstream繼承了類istream,類ofstream繼承了類ostream,類fstream繼承了類iostream。

   由圖3可以看到:由抽象基類ios直接派生出4個派生類,即istream,ostream,fstreambase和strstreambase。其中fstreambase是文件流類基類,由它再派生出ifstream,ofstream和fstream。strstreambase是字符串流類基類,由它再派生出lstrstream,ostrsCeam和swsWeam類。
   I/0類庫中還有其他一些類,但是對於一般用戶來說,以上這些已能滿足需要了。如果想深入瞭解類庫的內容和使用,可參閱所用的C++系統的類庫手冊。在本章將陸續介紹有關的類。

2、與iostream類庫有關的頭文件
   iostream類庫中不同的類的聲明被放在不同的頭文件中,用戶在自己的程序中用 #include命令包含了有關的頭文件就相當於在本程序中聲明瞭所需要用到的類。可以換一種說法:頭文件是程序與類庫的接口,iostream類庫的接口分別由不同的頭文件來實現。常用的有
  1. iostream 包含了對(標準)輸入輸出流進行操作所需的基本信息。
  2. fstream 用於用戶管理的文件的I/0操作。
  3. sbsbeam 用於字符串流I/0。
  4. stdiostream 用於混合使用C和C++的I/0機制時,例如想將C程序轉變爲C++程序。
  5. iomamp 在使用格式化I/0時應包含此頭文件。

3、在iostream頭文件中定義的流對象
   在iostream頭文件中定義的類有:ios,istream,ostream,iostream,istream_withassign,stream_withassign,iostream_withassign等。
   iostream包含了對輸入輸出流進行操作所需的基本信息。因此大多數C++程序都包括iostream。在iostream頭文件中不僅定義了有關的類,還定義了4種流對象,

   cin是istream的派生類istream_withassign的對象,它是從標準輸入設備(鍵盤)輸入到內存的數據流,稱爲cin流或標準輸入流。cout是ostream的派生類ostream_withassign的對象,它是從內存輸入到標準輸出設備(顯示器)的數據流,稱爲cout流或標準輸出流。cerr和clog作用相似,均爲向輸出設備(顯示器)輸出出錯信息。因此用鍵盤輸入時用cin流,向顯示器輸出時用cout流。向顯示器輸出出錯信息時用cerr和clog流。

   在iostream頭文件中定義以上4個流對象用以下的形式(以cout爲例):
  1. ostream cout(stdout);  
  2. //在定義cout爲ostream流類對象時,把標準輸出設備stdout作爲參數,這樣它就與標準輸出設備(顯示器)聯繫起來  
  3. cout<<3;//所以,就會在顯示器的屏幕上輸出3。  
4.在iostream頭文件中重載運算符

   “<<”和“>>”本來在C++中是被定義爲左位移運算符和右位移運算符的,由於在iostream頭文件中對它們進行了重載,使它們能用作標準類型數據的輸入和輸出運算符。所以,在用它們的程序中必須用#include命令把ostream包含到程序中。

  1. #include<iostream>  
   在istream和ostream類(這兩個類都是在頭文件iostream中聲明的)中分別有一組成員函數對位移運算符“<<”和“>>”進行重載,以便能用它輸入或輸出各種標準數據類型的數據。對於不同的標準數據類型要分別進行重載,如
  1. ostream operator<<(int); //用於向輸出流插入一個int數據  
  2. ostream operator<<(float);//用於向輸出流插入一個float數據  
  3. ostream operator<<(char); //用於向輸出流插入一個char數據  
  4. ostream operator<<(char *) //用於向輸出流插入一個字符串數據  
   等。如果在程序中有下面的表達式:         cout<<"C++";   根據第5章所介紹的知識,上面的表達式相當於 cout.operator<<("C++")   ”C++”的值是其首字節地址,是字符型指針(char *)類型,因此選擇調用上面最後一個運算符重載函數,通過重載函數的函數體,將字符串插入到cout流中,函數返回流對象cout。   在istream類中已將運算符“>>”重載爲對以下標準類型的提取運算符:char,signed char,unsigned char,short,unsigned short,int,unsigned int,long,unsigned long,float, double,longdouble,char*,signedchar*,unsignedchar*等。   在ostream類中將“<<”重載爲插入運算符,其適用類型除了以上的標準類型外,還增加了一個void。類型。   如果想將“<<”和“>>”用於自己聲明的類型的數據,就不能簡單地採用包含iostream頭文件來解決,必須自己用第5章介紹的方法對“<<”和“>>”進行重載。   怎樣理解運算符“<<”和“>>”的作用呢?有一個簡單而形象的方法:它們指出了數據移動的方向,例如:    >>a    // 箭頭方向表示把數據放入a中。而:    <<a    // 箭頭方向表示從a中拿出數據。標準輸出流:標準輸出流是流向標準輸出設備(顯示器)的數據。

四、cout,cerr和clog流

ostream類定義了3個輸出流對象,即cout,cerr,clog。分述如下。1、cout流對象   cout是console output的縮寫,意爲在控制檯(終端顯示器)的輸出。   1、cout不是C++預定義的關鍵字,它是ostream流類的對象,在iostream中定義。顧名思義,流是流動的數據,cout流是流向顯示器的數據。cout流是容納數據的載體,它並不是一個運算符。人們關心的是cout流中的內容,也就是向顯示器輸出什麼。   2、用"cout<<”輸出基本類型的數據時,可以不必考慮數據是什麼類型,系統會判斷數據的類型,並根據其類型選擇調用與之匹配的運算符重載函數。   這個過程都是自動的,用戶不必干預。如果在C語言中用prinf函數輸出不同類型的數據,必須分別指定相應的輸出格式符,十分麻煩,而且容易出錯。C++的I/0機制對用戶來說,顯然是方便而安全的。   3、cout流在內存中對應開闢了一個緩衝區,用來存放流中的數據。當向cout流插人一個endl時,不論緩衝區是否已滿,都支即輸出流中所有數據,然後插入一個換行符,並刷新流(清空緩衝區)。注意如果插入一個換行符,'\n'(如coot<<a<<'\n';),則只輸出a和換行,而不刷新cout流(但並不是所有編譯系統都體現出這一區別)。   4、在iostream中只對“<<”和“>>”運算符用於標準類型數據的輸入輸出進行了重載,但未對用戶聲明的類型數據的輸入輸出進行重載。如果用戶聲明瞭新的類型,並希望用“<<”和“>>”運算符對其進行輸入輸出,應該按照第5章介紹的方法,對“<<”和“>>”運算符另作重載。2、cerr流對象   cerr流對象是標準出錯流。cerr流已被指定爲與顯示器關聯。cerr的作用是向標準出錯設備(standard error device)輸出有關出錯信息。cerr是console error的縮寫,意爲“在控制檯(顯示器)顯示出錯信息”。cerr與標準輸出流cout的作用和用法差不多。但有一點不同:cout流通常是傳送到顯示器輸出,但也可以被重定向輸出到磁盤文件,而cerr流中的信息只能在顯示器輸出。當調試程序時,往往不希望程序運行時的出錯信息被送到其他文件,而要求在顯示器上及時輸出,這時應該用cerr。cerr流中的信息是用戶根據需要指定的。例1 有一元二次方程ax2+bx+c=0,其一般解爲 x、1、2= ……但若a=0,或b^2-4ac<0時,用此公式出錯。   編程序,從鍵盤輸入a,b,c的值,求x1和x2。如果a=0或b^2-4ac<0,輸出出錯信息。可寫出以下程序:
  1. #include <iostream>  
  2. #include <math.h>  
  3. using namespace std;  
  4. int main()  
  5. {    
  6.    float a,b,c,disc;  
  7.    cout<<"please input a,b,c:";  
  8.    cin>>a>>b>>c;  
  9.    if (a==0)  
  10.         cerr<<"a is equal to zero,error!"<<endl;//將出錯信息插入cerr,屏幕輸出  
  11.    else if ((disc=b*b-4*a*c)<0)  
  12.         cerr<<"disc=b*b-4*a*c<0"<<endl; //將出錯信息插入cerr流,屏幕輸出  
  13.    else  
  14.    {  
  15.         cout<<"x1="<<(-b+sqrt(disc))/(2*a)<<endl;  
  16.         cout<<"x2="<<(-b-sqrt(disc))/(2*a)<<endl;  
  17.     }  
  18.         return 0;   
  19. }  
3.clog流對象   clog流對象也是標準出錯流,它是console log的縮寫。它的作用和cerr相同,都是在終端顯示器上顯示出錯信息。它們之間只有一個微小的區別:ccrr是不經過緩衝區,直接向顯示器上輸出有關信息,而clog中的信息存放在緩衝區中,緩衝區滿後或遇endl時向顯示器輸出。五、格式輸出   在輸出數據時,爲簡便起見,往往不指定輸出的格式,由系統根據數據的類型採取默認的格式,但有時希望數據按指定的格式輸出,如要求以下六進制或八進制形式輸出一個整數,對輸出的小數只保留兩位小數等;有兩種方法可以達到此目的。一種是使用控制符;另一種是使用流對象的有關成員函數。分別敘述如下:

1、 用控制符控制輸出格式

應當注意:這些控制符是在頭文件iomanip中定義的,因而程序中應當包含頭文件iomanip。通過下面的例子可以瞭解使用它們的方法,

例2 用控制符控制輸出格式,
  1. #include <iostream>  
  2. #include<string>  
  3. #include <iomanip> //不要忘記包含此頭文件  
  4. using namespace std;  
  5. int main()  
  6.  {   
  7.    int a;  
  8.    cout<<"input a:";  
  9.    cin>>a;  
  10.    cout<<"dec:"<<dec<<a<<endl; //以上進制形式輸出整數  
  11.    cout<<"hex:"<<hex<<a<<endl; //以十六進制形式輸出整數a  
  12.    cout<<"oct:"<<setbase(8)<<a<<endl;//以八進制形式輸出整數a  
  13.    string pt= "China";         //pt指向字符串”China”  
  14.    cout<<setw(10)<<pt<<endl; //指定域寬爲10,輸出字符串  
  15.    cout<<setfill('*')<<setw(10)<<pt<<endl;//指定域寬10,輸出字符串,空白處以“*”填充  
  16.    double pi=22.0/7.0; //計算pi值  
  17.    cout<<setiosflags(ios::scientific)<<setprecision(8);//按指數形式輸出,8位小數  
  18.    cout<<"pi="<<pi<<endl; //輸出pi值  
  19.    cout<<"pi="<<setprecision(4)<<pi<<endl;//改爲4位小數  
  20.    cout<<"pi="<<setiosflags(ios::fixed)<<pi<<endl;//改爲小數形式輸出,精度爲4   
  21.    cout<<"pi="<<fixed<<pi<<endl;//fixed確定小數點後精度爲4   
  22.    return 0;   
  23. }  
運行結果如下:   inputa:34 (輸入a的值)   dec:34 (十進制形式)   hex:22 (十六進制形)   oct:42 (八進制形式)   China (域寬爲10)   ***** China (域寬爲10,空白處以'*'填充)   pi=3.14285714e+00 (指數形式輸出,8位小數)   pi=3.1429e+00) (指數形式輸小,4位小數)   pi=3.143 (小數形式輸出,精度仍爲4)   pi=3.1429(fixed確定小數點後精度爲4 )

2.用流對象的成員函數控制輸出格式   除了可以用控制符來控制輸出格式外,還可以通過調用流對象cout中用於控制輸出格式的成員函數來控制輸出格式。  流成員函數setf和控制符setiosflags括號中的參數表示格式狀態,它是通過格式標誌來指定的。格式標誌在類ios中被定義爲枚舉值。因此在引用這些格式標誌時要在前面加上類名ios和域運算符“::”。例3 用流控制成員函數輸出數據。
  1. #include <iostream>  
  2. using namespace std;  
  3. int main()  
  4.  {   
  5.    int a=21;  
  6.    cout.setf(ios::showbase); //設置輸出時的基數符號  
  7.    cout<<"dec:"<<a<<endl; //默認以十進制形式輸出a  
  8.    cout.unsetf(ios::dec); //終止十進制的格式設置  
  9.    cout.setf(ios::hex); //設置以十六進制輸出的狀態  
  10.    cout<<"hex:"<<a<<endl; //以十六進制形式輸出a  
  11.    cout.unsetf(ios::hex); //終止十六進制的格式設置  
  12.    cout.setf(ios::oct); //設置以八進制輸出的狀態  
  13.    cout<<"oct:"<<a<<endl; //以八進制形式輸出a  
  14.    cout.unsetf(ios::oct); //終止以八進制的輸出格式設置  
  15.    char *pt="China"//pt指向字符串”china”  
  16.    cout.width(10); //指定域寬爲10  
  17.    cout<<pt<<endl; //輸出字符串  
  18.    cout.width(10); //指定域寬爲10  
  19.    cout.fill('*'); //指定空白處以'*'填充  
  20.    cout<<pt<<endl; //輸出字符串  
  21.    double pi=22.0/7.0; //計算pi值  
  22.    cout.setf(ios::scientific);//指定用科學記數法輸出  
  23.    cout<<"pi="//輸出"pi="  
  24.    cout.width(14); //指定域寬爲14  
  25.    cout<<pi<<endl; //輸出"pi值  
  26.    cout.unsetf(ios::scientific); //終止科學記數法狀態  
  27.    cout.setf(ios::fixed); //指定用定點形式輸出  
  28.    cout.width(12); //指定域寬爲12  
  29.    cout.setf(ios::showpos); //在輸出正數時顯示“+”號  
  30.    cout.setf(ios::internal); //數符出現在左側  
  31.    cout.precision(6); //保留6位小數  
  32.    cout<<pi<<endl; //輸出pi,注意數符“+”的位置  
  33.    return 0;  
  34. }  
運行情況如下:
    dec:21 (十進制形式)
    hex:Oxl5 (十六進制形式,以0x開頭)
    oct:025 (八進制形式,以O開頭)
    China (域寬爲10)
    *****china (域寬爲10,空白處以'*'填充)
    pi=**3.142857e+00 (指數形式輸出,域寬14,默認6位小數)
    ****3.142857 (小數形式輸㈩,精度爲6,最左側輸出數符“+”)

說明:

   1、成員函數width(n)和控制符setw(n)只對其後的第一個輸出項有效。如果要求在輸出數據時都按指定的同一域寬n輸出,不能只調用一次width(n),而必須在輸出每一項前都調用一次width(n)。

   2、在表5中的輸出格式狀態分爲5組,每一組中同時只能選用一種(例如,dec,hex和oct中只能選一,它們是互相排斥的),在用成員函數serf和控制符setiosflags設置輸出格式狀態後,如果想改設置爲同組的另一狀態,應當調用成員函數unsetf(對應於成員函數serf)或resetiosflags(對應於控制符sefiosflags),先終止原來設置的狀態。然後再設置其他狀態。
   同理,程序倒數第8行的unsetf函數的調用也是不可缺少的。讀者不妨上機試一試。

   3、用serf函數設置格式狀態時,可以包含兩個或多個格式標誌,由於這些格式標誌在lOS類中被定義爲枚舉值,每一個格式標誌以一個二進位代表,因此可以用“位或”運算符“I”組合多個格式標誌

   4、可以看到:對輸出格式的控制,既可以用控制符(如例2),也可以用cout流的有關成員函數(如例3),二者的作用是相同的。控制符是在頭文件mmamp中定義的,因此用控制符時,必須包含iomanip頭文件。cout流的成員函數是在頭文件iostream中定義的,因此只需包含頭文件iostream,不必包含iomanip。許多程序人員感到使用控制符方便簡單,可以在一個cout輸出語句中連續使用多種控制符。

   5、關於輸山格式的控制,在使用中還會遇到一些細節問題,不可能在這裏全部涉及。在遇到問題時,請查閱專門手冊或上機試驗一下即可解決。

六、用流成員函數put輸出字符

   在程序中一般用cout和插入運算符“<<”實現輸出,cout流在內存中有相應的緩衝區。有時用戶還有特殊的輸出要求,例如只輸出一個字符。ostream類除了提供上面介紹過的用於格式控制的成員函數外,還提供了專用於輸出單個字符的成員函數put。如: cout.put('a');
   調用該函數的結果是在屏幕上顯示一個字符a。put函數的參數可以是字符或字符的ASCII代碼(也可以是一個整型表達式)。如: cout.put(65+32);
也顯示字符a,因爲97是字符a的ASCII代碼。

可以在一個語句中連續調用put函數。如
      cout.put(71),put(79).put(79).put(68).put('\n');
在屏幕上顯示GOOD。

例4 有一個字符串"BASIC",要求把它們按相反的順序輸出。
程序如下:
  1. #include <iostream>  
  2. using namespace std;  
  3. int main()  
  4.  {   
  5.    char *a="BASIC"//字符指引指向'B'  
  6.    for(int i=4;i>=0;i--)  
  7.    cout.put(*(a+i)); //從最後一個字符開始輸出  
  8.    cout.put('\n');  
  9.    return 0;   
  10. }  
運行時在屏幕上輸出:
  CISAB

 例4也可以改用putchar函數實現。程序如下:
  1. #include<iostream> //也可以用#include<stdio.h>,同時不要下一行  
  2. usmg namespace std;  
  3. int main()  
  4. {   
  5.    char *a="BASIC";  
  6.    for(int i=4;i>=0;i--)  
  7.    putchar(*(a+i));  
  8.    putchar('\n');  
  9. }  

   運行結果與前相同,成員函數put不僅可以用COUT流對象來調用,而且也可以用ostream類的其他流對象調用。

七、cin流

   在頭文件iostream.h中定義了cin,cout,cerr,clog4個流對象,cin輸人流,cout,cerr,clog是輸出流。關於coutl,cerr,clog的使用方法已在上一講中介紹。
   cin是istream類的對象,它從標準輸入設備(鍵盤)獲取數據,程序中的變量通過流提取符“>>”從流中提取數據。流提取符“>>”從流中提取數據時通常跳過輸人流中的空格、tab鍵、換行符等空白字符。注意:只有在輸入完數據再按回車鍵後,改行數據才被送人鍵盤緩衝區,形成輸入流,提取運算符“>>”才能從中提取數據。需要注意保證從流中讀取數據能雁常進行。
例如:    int a,b;
    cin>>a>>b; // 若從鍵盤上輸入 21 abc出錯
說明:
   只有在正常狀態時,才能從輸入流中提取數據。
   當遇到無效字符或遇到文件結束符(不是換行符,是文件中的數據已讀完)時,輸人流cin就處於出錯狀態,即無法正常提取數據。此時對cin流的所有提取操作都將終止。在IBMPC及其兼容機中,以Ctrl+Z表示文件結束符。在UNIX和Macintosh系統中,以Ctrl+D表示文件結束符。當輸人流cin處於出錯狀態時,如果測試cin的值,可以發現它的值爲false(假),即cia爲O值。如果輸入流在正常狀態,cin的值爲true(真),即cin爲一個非0值。可以通過測試cin的值,判斷流對象是否處於正常狀態和提取操作是否成功。


如:   if(!cin) //流cin處於出錯狀態,無法正常提取數據
  1. cout<<"error”;  

例5 通過測試cin的真值,判斷流對象是否處於正常狀態。
  1. #include <iostream>  
  2. using namespace std;  
  3. int main()  
  4. {   
  5.    float grade;  
  6.    cout<<"enter grade:";  
  7.    while(cin>>grade) //如果能從cin流讀取數據cin的值爲真,執行循環體  
  8.    {   
  9.      if(grade>=85) cout<<grade<<" GOOD!"<<endl;  
  10.      if(grade<60) cout<<grade<<" fail!"<<endl;  
  11.      cout<<"enter grade:";  
  12.    }  
  13.    cout<<"The end."<<endl;  
  14.    return 0;  
  15. }  

   流提取符“>>”不斷地從輸人流中提取數據(每次提取一個浮點數),如果成功,就賦給變量grade,此時cin爲真,若不成功則cin爲假。如果輸入文件結束符,表示數據已完。

運行情況如下:
   enter grade:67
   enter grade:89/
   89 GOOD!
   enter grade:56s/
   56 fail!
   entergrade:100
   100 GOOD!
   enter grade:^Z //輸入文件結束符
   The end.

在遇到文件結束符時,程序結束。如果某次輸入的數據爲: enter grade:100/2 (回車)
流提取符“>>”提取100,賦給grade,進行if語句的處理。然後再遇到“/”,認爲是無效字符,cin返回o。循環結束,輸出"Theend.”。
在不同的C++系統下運行此程序,在最後的處理上有些不同。以上是在GCC環境下運行程序的結果,如果在VC++環境下運行此程序,在鍵人Ctrl+z時,程序運行馬上結束,不輸出”Theend.”。

八、 用於字符輸入的流成員函數

除了可以用CIB輸入標準類型的數據外,還可以用istream類流對象的一些成員函數,實現字符的輸入。

1、用get函數讀入一個字符
流成員函數get有3種形式:無參數的,有一個參數的,有3個參數的。

(1)不帶參數的get函數

其調用形式爲: cin.get() //用來從指定的輸人流中提取一個字符(包括空白字符)函數的返回值就是讀入的字符。

   若遇到輸入流中的文件結束符,則函數值返回文件結束標誌EOF(End Of File),一般以-1代表EOF,用-1而不用0或正值,是考慮到不與字符的ASCII代碼混淆,但不同的C++系統所用的EOF值有可能不同。

例6 用get函數讀人字符。
   從鍵盤輸入一行字符,用cin.get()逐個讀人字符,將讀入字符賦給字符變量c。如果c的值不等於EOF(EOF是在lostream頭文件中定義的符號常量,代表-1),表示已成功地讀入一個有效字符,然後通過put函數輸出該字符。
  1. #include <iostream>  
  2. using namespace std;  
  3. int main()  
  4. {   
  5.    char c;  
  6.    cout<<"enter a sentence:"<<endl;  
  7.    while((c=cin.get())!=EOF)  
  8.    cout.put(c);  
  9.    return 0;   
  10. }  

運行情況如下:
   enter a sentence:
   I study C++ very hard. (輸入一行字符)
   I study C++ very hard. (輸出該行字符)
   ^Z/(程序結束)

   C語言中的getchar函數與流成員函數cin.get()的功能相同,C++保留廠C的這種用法,可以用getchar(c)從鍵盤讀取一個字符賦給變量c。

(2)有一個參數的get函數

其調用形式爲:
   cin.get(ch)// 其作用是從輸人流中讀取一個字符,賦給字符變量ch。

   如果讀取成功則函數返回非。值(真),如失敗(遇文件結束符)則函數返回。值(假)。例6可以改寫如下:
  1. #include<iostream>  
  2. int main()  
  3. {   
  4.    char c;  
  5.    cout<<"enter a sentence:"<<endl;  
  6.    while(cin.get(c)) //讀取—個字符賦給字符變量c,如果讀取成功,cin.get(c)爲真  
  7.    {   
  8.         cout.put(c);  
  9.     }  
  10.    cout<<"end"<<endl:  
  11.    return 0;  
  12. }  

(3)有3個參數的get函數

其調用形式爲
     cin.get(字符數組,字符個數n,終止字符)
   或
     cin.get(字符指針,字符個數n,終止字符)

   其作用是從輸入流中讀取n-1個字符,賦給指定的字符數組(或字符指針指向的數組),如果在讀取n-1個字符之前遇到指定的終止字符,則提前結束讀取。如果讀取成功則函數返回非0值(真),如失敗(遇文件結束符)則函數返回0值(假)。再將例6改寫如下:
  1. #include<iostream>  
  2. using namespace std;  
  3. int main()  
  4. {   
  5.    char ch[20];  
  6.    cout<<"enter a sentence:”<<endl;  
  7.    cin.get(ch,10,'\n'); //指定換行符爲終止字符  
  8.    cout<<ch<<endl;  
  9.    return();   
  10. }  
運行情況如下:
   enter a sentence:
   I study C++ very hard.
   I study C

    在輸人流中有22個字符,但由於在get函數中指定的n爲10,讀取n-1個(即9個)字符並賦給字符數組ch中前9個元素。有人可能要問:指定n=10,爲什麼只讀取9個字符呢?因爲存放的是一個字符串,因此在9個字符之後要加入一個字符中結束標誌 '\0',實際上存放到數組中的是10個字符。請讀者思考:如果不加入字符串結束標誌,會出現什麼情況?結果是:在用"cout<<ch”;輸出數組中的字符時,不是輸出讀人的字符串,而是數組中的全部元素。讀者可以上機檢查一下ch[9](即數組中第10個元素)的值是什麼?
如果輸入: abcde/

2.用成員函數getline函數讀入一行字符
   getline函數的作用是從輸人流中讀取一行字符,其用法與帶3個參數的get函數類 似。

   即: cin.getline(字符數組(或字符指針),字符個數n,終止標誌字符)

例7 用getline函數讀入一行字符。
  1. #include <iostream>  
  2. using namespace std;  
  3. int main()  
  4. {   
  5.    char ch[20];  
  6.    cout<<"enter a sentence:"<<endl;  
  7.    cin>>ch;  
  8.    cout<<"The string read with cin is:"<<ch<<endl;  
  9.    cin.getline(ch,20,'/');//讀19個字符或遇'/'結束  
  10.    cout<<"The second part is:"<<ch<<endl;  
  11.    cin.getline(ch,20); //讀l9個字符或遇'/n',結束  
  12.    cout<<"The third part is:"<<ch<<endl;  
  13.    return 0;   
  14. }  
程序運行情況如下:
enter a sentence:I like C++./I study C++./I am happy.(回車)
The stung read with cin is:I
The second part is:like C++.
The third part is:I study C++./I am h

有幾點說明並請思考:
   1、如果第2個cin.getline函數也寫成cin.getline(ch,20,'/'),輸出結果會如何?
此時最後一行的輸出爲:
The third part is:I study C++

   2、如果在用cin.getline(ch,20,'/')從輸入流讀取數據時,遇到回車鍵('\n'),是否結束讀取?結論是此時'/n'不是結束標誌。'/n'被作爲一個字符被讀人。

   3、用gefiine函數從輸入流讀字符時,遇到終止標誌字符時結束,指針移到該終止標誌字符之後,下一個getline函數將從該終止標誌的下一個字符開始接着讀人,如本程序運行結果所示那樣。如果用cln.get函數從輸人流讀字符時,遇終止標誌字符時停止讀取,指針不向後移動,仍然停留在原位置。下一次讀取時仍從該終止標誌字符開始。這是getline函數和get函數不同之處。   
   因此用get函數時要特別注意,必要時用其他方法跳過該終止標誌字符(如用下面介紹的ignore函數。但一般來說還是用getline函數更方便。

   4、請比較用"cin<<”和用成員函數cin.getline()讀數據的區別。用"cin<<”讀數據時以空白字符(包括空格、tab鍵、回車鍵)作爲終止標誌,而用cln.gefline()讀數據時連續讀取一系列字符,可以包括空格。用"cin<<”可以讀取C++的標準類型的各類型數據(如果經過重載,還可以用於輸入自定義類型的數據),而用cin.getline()只用於輸入字符型數據。

九、istrearn類的其他成員函數

   除了以上介紹的用於讀取數據的成員函數外,lstream類還有其他在輸入數據時用得着的一些成員函數。常用的有以下幾種:

1.eof函數
   eof是end of file的縮寫,表示“文件結束”。從輸人流讀取數據,如果到達文件末尾(遇文件結束符),eof函數值爲非零值(表示真),否則爲o(假)。這個函數是很有用的,經常會用到。

例8 逐個讀入一行字符,將其中的非空格字符輸出。
  1. #include <iostream>  
  2. using namespace std;  
  3. int main()  
  4. {   
  5.    char c;  
  6.    while(!cin.eof())    //eof()爲假表示未遇到文件結束符  
  7.    if((c=cin.get())!=' ')//檢查讀入的字符是否爲空格字符  
  8.       cout.put(c);  
  9.    return 0;   
  10. }  
運行情況如下:
   C++ is very interesting.
   C++ is veryinteresting.
   ^z(結束)

2.peek函數
   peek是“觀察”的意思,peek函數的作用是觀測下一個字符。其調用形式爲
      c=cin.peek();
   cin.peek函數的返回值是指針指向的當前字符,但它只是觀測,指針仍停留在當前位置,並不後移。如果要訪問的字符是文件結束符,則函數值是EOF(-1)。

3.putback函數
   其調用形式爲
      cin.putback(ch);
   其作用是將前面用get或getline函數從輸人流中讀取的字符ch返回到輸人流,插入到當前指針位置,以供後面讀取。

例9 peek函數和putback函數的用法。
  1. #include <iostream>  
  2. using namespace std;  
  3. int main()  
  4. {   
  5.    char c[20];  
  6.    int ch;  
  7.    cout<<"please enter a sentence."<<endl;  
  8.    cin.getline(c,15,'/');  
  9.    cout<<"The first part is:"<<c<<endl;  
  10.    ch=cin.peek(); //觀看當前字符  
  11.    cout<<"The next character(ASCII code) is:"<<ch<<endl;  
  12.    cin.putback(c[0]);//將'I'插入到指針所指處  
  13.    cin.getline(c,15,'/');  
  14.    cout<<"The second part is:"<<c<<endl;  
  15.    return 0;   
  16. }  
運行情況如下:
   please enter a sentence:
   I am a boy./I am astudent./ (回車)
   The first part is:I am a boy.
   The next chamcter(ASCII code) is:32 (下一個字符是空格)
   The second part is:I an a student

4.ignore函數

   其調用形式爲: cin.ignore(n,終止字符) // 函數作用是跳過輸人流中n個字符,或在遇到指定的終止字符時提前結束(此時跳過包括終止字符在內的若干字符)。
如:
      ighore(5,A,) //跳過輸入流中5個字符,遇A後就不再跳了
   也可以不帶參數或只帶一個參數。如
      ignore() (n默認值爲1,終止字符默認爲EOF)
   相當於:  ignore(1,EOF)

例10 用ignore函數跳過輸入流中的字符。
先看不用ignore函數的情況:
  1. #include <iostream>  
  2. using namespace std;  
  3. int main()  
  4. {   
  5.    char ch[20];  
  6.    cin.get(ch,20,'/');  
  7.    cout<<"The first part is:"<<ch<<endl;  
  8.    cin.get(ch,20,'/');  
  9.    cout<<"The second part is:"<<ch<<endl;  
  10.    return 0;  
  11. }  
運行結果如下:
   I like C++./I study C++./I am happy.
   The first part is: I like C++
   The second part is (字符數組ch中沒有從輸人流中讀取有效字符)

   前面已對此作過說明。如果希望第二個tin.get函數能讀取”I study C++.”,就應該設法跳過輸人流中第一個'/'可以用ignore函數來實現此日的,將程序改爲:
  1. #include <iostream>  
  2. using namespace std;  
  3. int main()  
  4. {   
  5.    char ch[20];  
  6.    cin.get(ch,20,'/');  
  7.    cout<<"The first part is:"<<ch<<endl;  
  8.    cin.ignore(); //跳過輸入流中一個字符  
  9.    cin.get(ch,20,'/');  
  10.    cout<<"The second part is:"<<ch<<endl;  
  11.    return 0;   
  12. }  
運行結果如下:
   I like C++./I study C++./I am happy.
   The first part is:I like C++.
   The second part is: I study C++.

以上介紹的各個成員函數,不僅可以用cin流對象來調用,而且也可以用istream類的其他流對象調用。

原文鏈接:C++的輸入和輸出與標準輸出流 - CSDN博客
https://blog.csdn.net/yas12345678/article/details/52592739

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章