天天看點

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

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

   以前所用到的輸入和輸出,都是以終端為對象的,即從鍵盤輸入資料,運作結果輸出到顯示器螢幕上。從作業系統的角度看,每一個與主機相連的輸入輸出裝置都被看作一個檔案。程式的輸入指的是從輸入檔案将資料傳送給程式,程式的輸出指的是從程式将資料傳送給輸出檔案。C++的輸入與輸出包括以下3方面的内容:

   1、對系統指定的标準裝置的輸入和輸出。簡稱标準I/O。(裝置)

   2、以外存磁盤(或CD光牒)檔案為對象進行輸入和輸出。簡稱檔案I/0。(檔案)

   3、對記憶體中指定的空間進行輸入和輸出。簡稱串I/O。(記憶體)

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

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

C語言中I/O存在問題:

   1、在C語言中,用prinff和scanf進行輸入輸出,往往不能保證所輸入輸出的資料是可靠的、安全的。學過C語言的讀者可以分析下面的用法:想用格式符%d輸出一個整數,但不小心錯用了它輸出單精度變量和字元串,會出現什麼情況?假定所用的系統int型占兩個位元組。

printf("%d",i);   //i為整型變量,正确,輸出i的值
printf("%d",f);   //f為單精度變量,輸出變量中前兩個位元組的内容
printf("%d","C++");//輸出字元串"C++”的起始位址
           

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

   2、在用scanf輸入時,有時出現的問題是很隐蔽的。如

scanf("%d",&i); //正确,輸入一個整數,賦給整型變量i
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,就将緩沖區中的全部資料送到顯示器顯示出來。在輸入時,從鍵盤輸入的資料先放在鍵盤緩沖區中,當按Enter鍵時,鍵盤緩沖區中的資料輸入到程式中的輸入緩沖區,形成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為例):

ostream cout(stdout);
//在定義cout為ostream流類對象時,把标準輸出裝置stdout作為參數,這樣它就與标準輸出裝置(顯示器)聯系起來
cout<<3;//是以,就會在顯示器的螢幕上輸出3。
           

4.在iostream頭檔案中重載運算符

   “<<”和“>>”本來在C++中是被定義為左位移運算符和右位移運算符的,由于在iostream頭檔案中對它們進行了重載,使它們能用作标準類型資料的輸入和輸出運算符。是以,在用它們的程式中必須用#include指令把ostream包含到程式中。

#include<iostream>
           

   在istream和ostream類(這兩個類都是在頭檔案iostream中聲明的)中分别有一組成員函數對位移運算符“<<”和“>>”進行重載,以便能用它輸入或輸出各種标準資料類型的資料。對于不同的标準資料類型要分别進行重載,如

ostream operator<<(int); //用于向輸出流插入一個int資料
ostream operator<<(float);//用于向輸出流插入一個float資料
ostream operator<<(char); //用于向輸出流插入一個char資料
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,輸出出錯資訊。可寫出以下程式:

#include <iostream>
#include <math.h>
using namespace std;
int main()
{  
   float a,b,c,disc;
   cout<<"please input a,b,c:";
   cin>>a>>b>>c;
   if (a==0)
        cerr<<"a is equal to zero,error!"<<endl;//将出錯資訊插入cerr,螢幕輸出
   else if ((disc=b*b-4*a*c)<0)
        cerr<<"disc=b*b-4*a*c<0"<<endl; //将出錯資訊插入cerr流,螢幕輸出
   else
   {
        cout<<"x1="<<(-b+sqrt(disc))/(2*a)<<endl;
        cout<<"x2="<<(-b-sqrt(disc))/(2*a)<<endl;
    }
        return 0; 
}
           

3.clog流對象

   clog流對象也是标準出錯流,它是console log的縮寫。它的作用和cerr相同,都是在終端顯示器上顯示出錯資訊。它們之間隻有一個微小的差別:ccrr是不經過緩沖區,直接向顯示器上輸出有關資訊,而clog中的資訊存放在緩沖區中,緩沖區滿後或遇endl時向顯示器輸出。

五、格式輸出

   在輸出資料時,為簡便起見,往往不指定輸出的格式,由系統根據資料的類型采取預設的格式,但有時希望資料按指定的格式輸出,如要求以下六進制或八進制形式輸出一個整數,對輸出的小數隻保留兩位小數等;有兩種方法可以達到此目的。一種是使用控制符;另一種是使用流對象的有關成員函數。分别叙述如下:

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

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

例2 用控制符控制輸出格式,

#include <iostream>
#include<string>
#include <iomanip> //不要忘記包含此頭檔案
using namespace std;
int main()
 { 
   int a;
   cout<<"input a:";
   cin>>a;
   cout<<"dec:"<<dec<<a<<endl; //以上進制形式輸出整數
   cout<<"hex:"<<hex<<a<<endl; //以十六進制形式輸出整數a
   cout<<"oct:"<<setbase(8)<<a<<endl;//以八進制形式輸出整數a
   string pt= "China";         //pt指向字元串”China”
   cout<<setw(10)<<pt<<endl; //指定域寬為10,輸出字元串
   cout<<setfill('*')<<setw(10)<<pt<<endl;//指定域寬10,輸出字元串,空白處以“*”填充
   double pi=22.0/7.0; //計算pi值
   cout<<setiosflags(ios::scientific)<<setprecision(8);//按指數形式輸出,8位小數
   cout<<"pi="<<pi<<endl; //輸出pi值
   cout<<"pi="<<setprecision(4)<<pi<<endl;//改為4位小數
   cout<<"pi="<<setiosflags(ios::fixed)<<pi<<endl;//改為小數形式輸出,精度為4 
   cout<<"pi="<<fixed<<pi<<endl;//fixed确定小數點後精度為4 
   return 0; 
}
           

運作結果如下:   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 用流控制成員函數輸出資料。

#include <iostream>
using namespace std;
int main()
 { 
   int a=21;
   cout.setf(ios::showbase); //設定輸出時的基數符号
   cout<<"dec:"<<a<<endl; //預設以十進制形式輸出a
   cout.unsetf(ios::dec); //終止十進制的格式設定
   cout.setf(ios::hex); //設定以十六進制輸出的狀态
   cout<<"hex:"<<a<<endl; //以十六進制形式輸出a
   cout.unsetf(ios::hex); //終止十六進制的格式設定
   cout.setf(ios::oct); //設定以八進制輸出的狀态
   cout<<"oct:"<<a<<endl; //以八進制形式輸出a
   cout.unsetf(ios::oct); //終止以八進制的輸出格式設定
   char *pt="China"; //pt指向字元串”china”
   cout.width(10); //指定域寬為10
   cout<<pt<<endl; //輸出字元串
   cout.width(10); //指定域寬為10
   cout.fill('*'); //指定空白處以'*'填充
   cout<<pt<<endl; //輸出字元串
   double pi=22.0/7.0; //計算pi值
   cout.setf(ios::scientific);//指定用科學記數法輸出
   cout<<"pi="; //輸出"pi="
   cout.width(14); //指定域寬為14
   cout<<pi<<endl; //輸出"pi值
   cout.unsetf(ios::scientific); //終止科學記數法狀态
   cout.setf(ios::fixed); //指定用定點形式輸出
   cout.width(12); //指定域寬為12
   cout.setf(ios::showpos); //在輸出正數時顯示“+”号
   cout.setf(ios::internal); //數符出現在左側
   cout.precision(6); //保留6位小數
   cout<<pi<<endl; //輸出pi,注意數符“+”的位置
   return 0;
}
           

運作情況如下:

    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",要求把它們按相反的順序輸出。

程式如下:

#include <iostream>
using namespace std;
int main()
 { 
   char *a="BASIC"; //字元指引指向'B'
   for(int i=4;i>=0;i--)
   cout.put(*(a+i)); //從最後一個字元開始輸出
   cout.put('\n');
   return 0; 
}
           

運作時在螢幕上輸出:

  CISAB

 例4也可以改用putchar函數實作。程式如下:

#include<iostream> //也可以用#include<stdio.h>,同時不要下一行
usmg namespace std;
int main()
{ 
   char *a="BASIC";
   for(int i=4;i>=0;i--)
   putchar(*(a+i));
   putchar('\n');
}
           

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

七、cin流

   在頭檔案iostream.h中定義了cin,cout,cerr,clog4個流對象,cin輸人流,cout,cerr,clog是輸出流。關于coutl,cerr,clog的使用方法已在上一講中介紹。

   cin是istream類的對象,它從标準輸入裝置(鍵盤)擷取資料,程式中的變量通過流提取符“>>”從流中提取資料。流提取符“>>”從流中提取資料時通常跳過輸人流中的空格、tab鍵、換行符等空白字元。注意:隻有在輸入完資料再按Enter鍵後,改行資料才被送人鍵盤緩沖區,形成輸入流,提取運算符“>>”才能從中提取資料。需要注意保證從流中讀取資料能雁常進行。

例如:    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處于出錯狀态,無法正常提取資料

cout<<"error”;
           

例5 通過測試cin的真值,判斷流對象是否處于正常狀态。

#include <iostream>
using namespace std;
int main()
{ 
   float grade;
   cout<<"enter grade:";
   while(cin>>grade) //如果能從cin流讀取資料cin的值為真,執行循環體
   { 
     if(grade>=85) cout<<grade<<" GOOD!"<<endl;
     if(grade<60) cout<<grade<<" fail!"<<endl;
     cout<<"enter grade:";
   }
   cout<<"The end."<<endl;
   return 0;
}
           

   流提取符“>>”不斷地從輸人流中提取資料(每次提取一個浮點數),如果成功,就賦給變量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函數輸出該字元。

#include <iostream>
using namespace std;
int main()
{ 
   char c;
   cout<<"enter a sentence:"<<endl;
   while((c=cin.get())!=EOF)
   cout.put(c);
   return 0; 
}
           

運作情況如下:

   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可以改寫如下:

#include<iostream>
int main()
{ 
   char c;
   cout<<"enter a sentence:"<<endl;
   while(cin.get(c)) //讀取—個字元賦給字元變量c,如果讀取成功,cin.get(c)為真
   { 
        cout.put(c);
    }
   cout<<"end"<<endl:
   return 0;
}
           

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

其調用形式為

     cin.get(字元數組,字元個數n,終止字元)

   或

     cin.get(字元指針,字元個數n,終止字元)

   其作用是從輸入流中讀取n-1個字元,賦給指定的字元數組(或字元指針指向的數組),如果在讀取n-1個字元之前遇到指定的終止字元,則提前結束讀取。如果讀取成功則函數傳回非0值(真),如失敗(遇檔案結束符)則函數傳回0值(假)。再将例6改寫如下:

#include<iostream>
using namespace std;
int main()
{ 
   char ch[20];
   cout<<"enter a sentence:”<<endl;
   cin.get(ch,10,'\n'); //指定換行符為終止字元
   cout<<ch<<endl;
   return(); 
}
           

運作情況如下:

   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函數讀入一行字元。

#include <iostream>
using namespace std;
int main()
{ 
   char ch[20];
   cout<<"enter a sentence:"<<endl;
   cin>>ch;
   cout<<"The string read with cin is:"<<ch<<endl;
   cin.getline(ch,20,'/');//讀19個字元或遇'/'結束
   cout<<"The second part is:"<<ch<<endl;
   cin.getline(ch,20); //讀l9個字元或遇'/n',結束
   cout<<"The third part is:"<<ch<<endl;
   return 0; 
}
           

程式運作情況如下:

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,'/')從輸入流讀取資料時,遇到Enter鍵('\n'),是否結束讀取?結論是此時'/n'不是結束标志。'/n'被作為一個字元被讀人。

   3、用gefiine函數從輸入流讀字元時,遇到終止标志字元時結束,指針移到該終止标志字元之後,下一個getline函數将從該終止标志的下一個字元開始接着讀人,如本程式運作結果所示那樣。如果用cln.get函數從輸人流讀字元時,遇終止标志字元時停止讀取,指針不向後移動,仍然停留在原位置。下一次讀取時仍從該終止标志字元開始。這是getline函數和get函數不同之處。   

   是以用get函數時要特别注意,必要時用其他方法跳過該終止标志字元(如用下面介紹的ignore函數。但一般來說還是用getline函數更友善。

   4、請比較用"cin<<”和用成員函數cin.getline()讀資料的差別。用"cin<<”讀資料時以空白字元(包括空格、tab鍵、Enter鍵)作為終止标志,而用cln.gefline()讀資料時連續讀取一系列字元,可以包括空格。用"cin<<”可以讀取C++的标準類型的各類型資料(如果經過重載,還可以用于輸入自定義類型的資料),而用cin.getline()隻用于輸入字元型資料。

九、istrearn類的其他成員函數

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

1.eof函數

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

例8 逐個讀入一行字元,将其中的非空格字元輸出。

#include <iostream>
using namespace std;
int main()
{ 
   char c;
   while(!cin.eof())    //eof()為假表示未遇到檔案結束符
   if((c=cin.get())!=' ')//檢查讀入的字元是否為空格字元
      cout.put(c);
   return 0; 
}
           

運作情況如下:

   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函數的用法。

#include <iostream>
using namespace std;
int main()
{ 
   char c[20];
   int ch;
   cout<<"please enter a sentence."<<endl;
   cin.getline(c,15,'/');
   cout<<"The first part is:"<<c<<endl;
   ch=cin.peek(); //觀看目前字元
   cout<<"The next character(ASCII code) is:"<<ch<<endl;
   cin.putback(c[0]);//将'I'插入到指針所指處
   cin.getline(c,15,'/');
   cout<<"The second part is:"<<c<<endl;
   return 0; 
}
           

運作情況如下:

   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函數的情況:

#include <iostream>
using namespace std;
int main()
{ 
   char ch[20];
   cin.get(ch,20,'/');
   cout<<"The first part is:"<<ch<<endl;
   cin.get(ch,20,'/');
   cout<<"The second part is:"<<ch<<endl;
   return 0;
}
           

運作結果如下:

   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函數來實作此日的,将程式改為:

#include <iostream>
using namespace std;
int main()
{ 
   char ch[20];
   cin.get(ch,20,'/');
   cout<<"The first part is:"<<ch<<endl;
   cin.ignore(); //跳過輸入流中一個字元
   cin.get(ch,20,'/');
   cout<<"The second part is:"<<ch<<endl;
   return 0; 
}
           

運作結果如下:

   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類的其他流對象調用。