各種字符讀取方法的比較(cin,getline等)

讀取一個字符或一個字符串的方法有很多,也有些陷阱,下面總結一下。

 

(1)>>操作符

>>操作符的重載,有很多種原型,能夠從輸入流抽取各種形式的輸入(int、單個字符、字符串等),也是我們最常用的一種讀取字符的方式。它需要配合輸入流對象使用(cin就是iostream類中的istream類對象靜態成員),並且返回值是輸入流的引用,所以能夠有cin>>a>>b這樣的寫法。

結束符:cin>>遇到“回車”(\n) 結束輸入,另外遇到“空格”、“TAB”(\t)、之後就不再接收字符。所以如果希望輸入帶空格的字符串,應當使用其它的方法。

對字符的處理:cin>>會忽略第一個有效字符前的其他字符(如空格,回車等),而在輸入結束時,結束符會留在輸入流中。

舉兩個例子:

#include "stdafx.h"

#include<iostream>

using namespace std;

int _tmain(int argc,_TCHAR* argv[])

{

         char a[20],b[20];

         cin>>a>>b;

         cout<<a<<b<<endl;

         return0;

}

輸入與輸出:

原因就是開始cin>>讀取輸入到a中,遇到空格後,a的輸入結束,cin>>忽略這個空格,開始讀取輸入到b。

 

而如果是

int _tmain(int argc,_TCHAR* argv[])

{

         char a[20],b[20];

         cin>>a;

         cin.getline(b,20);

         cout<<a<<endl;

         cout<<b<<endl;

         return 0;

}

如果輸入爲hello,那麼結果爲,也就是a等於hello,而b爲空,原因就是輸入末尾的換行符\n留在了輸入流中,導致b的輸入直接結束。

如果輸入爲hello world,那麼結果爲,a等於hello,而b爲空格world,原因是cin.getline不會忽略開頭的空格。cin>>後使用其他函數進行輸入也類似,事實上,只有cin>>會忽略有效字符前的其它字符。

 

(2) istream:: getline

istream::getline是istream類的public成員函數(iostream類又繼承了istream類,通過cin對象來調用istream::getline),其原型有兩種形式:

istream& getline (char* s, streamsize n);

istream& getline (char* s, streamsizen, char delim );

用於從輸入流讀取指定長度n-1的字符串到s所指向的字符變量中。

結束符:其中第一種聲明默認\n爲結束符,而第二種形式則可以通過第三個參數delim來指定結束符。

對字符的處理:前面的例子已經說明了cin.getline不會忽略有效字符前的其它字符,那麼輸入結束時的情況如何,看兩個例子便知。

int _tmain(int argc,_TCHAR* argv[])

{

         chara[20],b[20];

         cin.getline(a,20);

         cin.getline(b,20);

         cout<<a<<b<<endl;

         return0;

}

輸入與輸出:

可以看出cin.getline能夠接收空格,另外a輸入結束時丟棄了結束符\n,所以b能夠進行正常的輸入(當然,如果連續輸入兩個回車,那b就爲空了)。值得注意的是,這裏說的b爲空是隻包含\0結束符,因爲當參數n大於0時,cin.getline函數會自動往字符串末尾添加\0,這也符合C風格字符串的要求。

需要注意的是,當輸入字符串爲空,以及輸入字符串的長度超出n-1時,failbit標誌位會被置位,將會影響到之後的輸入。

 

(3) istream:: get

istream:: get同樣是istream類的public成員函數,其也有多種原型:

能夠讀取單個字符,也能讀取C風格字符串,還能直接從流緩存中讀取,前兩種功能比較常用。

結束符:讀取單個字符時,也就無所謂結束符。當讀取字符串時,與getline一樣,第一種聲明默認\n爲結束符,而第二種形式則可以通過第三個參數delim來指定結束符。

對字符的處理:在輸入字符串時,cin.get不會忽略有效字符前的字符,同時在輸入結束時會將\n留在輸入流中。看個例子驗證一下。

int _tmain(int argc,_TCHAR* argv[])

{

         chara[20],b[20];

         cin.get(a,20);

         cin.get(b,20);

         cout<<b<<endl;

         cout<<a<<endl;

         return0;

}

如果輸入爲hello world,那麼結果爲,a等於hello world,而b爲\n。

原因也是遺留的\n導致了b輸入的直接結束。

 

但是需要注意的是,採用不帶參數的cin.get()讀入一個字符的時候,會有不同的行爲,看幾個例子:

int _tmain(int argc,_TCHAR* argv[])

{

         char b[20];

         char a;

         cin.get(b,20);

         cin.get(a);

         cout<<a<<b<<endl;

         return0;

}

如果輸入hello world回車,則結果爲:。可以看到b就等於hello world,而a等於\n,並將其原原本本的輸出,因爲\n對cin.get()來說並不是結束符。

如果輸入從a到z的26個字母,則結果爲:。a爲第20個字母t,b爲前19個字母。所以get函數與getline函數不同,輸入字符串長度超出n-1也能正常運行。

如果將接收的順序反過來會怎麼樣呢,

int _tmain(int argc,_TCHAR* argv[])

{

         char b[20];

         char a;

         cin.get(a);

         cin.get(b,20);

         cout<<b<<a<<endl;

         return0;

}

如果輸入hello world回車,很明顯結果爲:。說明a得到第一個輸入字符後,剩下的輸入給了b。

另外cin.get(a)還能寫成a=cin.get(),但是無參數形式的返回值是int類型,這裏面會發生隱式轉換。

 

(4)std::getline

對於string類對象,我們還經常用另一個getline函數,用於讀入字符串到string類對象中。這個getline函數明顯是不屬於istream類的。

其原型如下:

istream& getline(istream&  is, string& str);

istream& getline(istream&& is, string& str);

istream& getline(istream&  is, string& str, chardelim);

istream& getline(istream&& is, string& str, char delim);

 

結束符:與getline一樣,前兩種聲明默認\n爲結束符,而後兩種聲明則可以通過第三個參數delim來指定結束符。

對字符的處理:在輸入字符串時,std::getline不會忽略有效字符前的字符,同時在輸入結束時\n會從輸入流中取出並丟棄。

處理方式和 istream:: getline是一樣的,因此這裏就不再給出例程,唯一要注意的就是函數原型的區別。


(5)getchar,gets(已被移除)等

需要包含<cstdio>頭文件,這些函數屬於C庫函數,C++程序中應當儘量避免使用。

主要看一下getchar函數,它的原型很簡單:

int getchar ( void );

對字符的處理:getchar的行爲和cin.get()類似,如果stdin流(cin與其同步對應)中沒有字符,程序會等待用戶進行輸入,直到用戶輸入回車,getchar纔開始從stdin流中按順序取出一個字符,餘下的字符會殘留在stdin流中,將會被後續的輸入函數取出。getchar函數的返回值是字符的ASCII碼,如出錯返回-1。如果stdin流中已經有字符,比如之前輸入殘留在流中的\n,那麼getchar會直接將其取出並返回。

看兩個例子能夠驗證以上說法:

int _tmain(int argc,_TCHAR* argv[])

{

         chara,b[20],c;

         a=getchar();

         c=getchar();

         cin.getline(b,20);

         cout<<b<<a<<c<<endl;

         return0;

}

輸入與輸出爲:

即b等於llo world,a與c分別等於h與e。

int _tmain(int argc,_TCHAR* argv[])

{

         chara,b[20];

         cin>>b;

         a=getchar();

         cout<<a<<b<<endl;

         return0;

}

輸入爲hello回車,結果爲。也就是a等於\n,b等於hello。

 

總的來說,除了cin>>之外,其他函數都不會忽略第一個有效字符之前的字符,也就是會讀取之前輸入殘留的換行符\n(除非之前使用的是getline函數),這往往是引起問題的根源。一般我們可以在兩個輸入函數之間加入一句cin.get()或cin.ignore()來吃掉這個換行符。


getline函數的作用是從輸入流中讀取一行字符,其用法與帶3個參數的get函數類似。即
    cin.getline(字符數組(或字符指針), 字符個數n, 終止標誌字符)

[例13.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,'/'); //讀個字符或遇'/'結束
  10. cout<<"The second part is:"<<ch<<endl;
  11. cin.getline(ch,20); //讀個字符或遇'/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 string read with cin is:I
The second part is: like C++.
The third part is:I study C++./I am h

請仔細分析運行結果。用“cin>>”從輸入流提取數據,遇空格就終止。因此只讀取 一個字符'I',存放在字符數組元素ch[0]中,然後在ch[1]中存放'\0'。因此用"cout<<ch"輸出時,只輸出一個字符'I'。然後用cin.getline(ch, 20, '/')從輸入流讀取19個字符 (或遇結束)。請注意:此時並不是從輸入流的開頭讀取數據。在輸入流中有一個字符指針,指向當前應訪問的字符。在開始時,指針指向第一個字符,在讀入第一個字符'I'後,指針就移到下一個字符('I'後面的空格),所以getline函數從空格讀起,遇到就停止,把字符串" like c++."存放到ch[0]開始的10個數組元素中,然後用"cout<<ch"輸出這10個字符。注意:遇終止標誌字符"/"時停止讀取並不放到數組中。再用cin.getline(ch, 20)讀19個字符(或遇'/n'結束),由於未指定以'/'爲結束標誌,所以第2個'/'被當作一般字符讀取,共讀入19個字符,最後輸出這19個字符。

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

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

3) 用getline函數從輸入流讀字符時,遇到終止標誌字符時結束,指針移到該終止標誌字符之後,下一個getline函數將從該終止標誌的下一個字符開始接着讀入,如本程序運行結果所示那樣。如果用cin.get函數從輸入流讀字符時,遇終止標誌字符時停止讀取,指針不向後移動,仍然停留在原位置。下一次讀取時仍從該終止標誌字符開始。這是getline函數和get函數不同之處。假如把例13.7程序中的兩個cin.line函數調用都改爲以下函數調用:
    cin.getline(ch, 20, '/');
則運行結果爲:
enter a sentence: I like C++./I study C++./I am happy.↙
The string read with cin is: I
The second part is: like C++.
The third part is:    (沒有從輸人流中讀取有效字符)

第2個cin. getline(ch, 20, '/')從指針當前位置起讀取字符,遇到的第1個字符就是終止標誌字符讀入結束,只把"\0"存放到ch[0]中,所以用“cout<<ch”輸出時無字符輸出。

因此用get函數時要特別注意,必要時用其他方法跳過該終止標誌字符(如用後面介紹的ignore函數,詳情請查看:一些與輸入有關的istream類成員函數),但一般來說還是用getline函數更方便。

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



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