輸入原理:
程序的輸入都建有一個緩沖區,即輸入緩沖區。一次輸入過程是這樣的,當一次鍵盤輸入結束時會將輸入的數據存入輸入緩沖區,而cin函數直接從輸入緩沖區中取數據。正因為cin函數是直接從緩沖區取數據的,所以有時候當緩沖區中有殘留數據時,cin函數會直接取得這些殘留數據而不會請求鍵盤輸入
1. cin>>
該操作符是根據后面變量的類型讀取數據。
輸入結束條件 :遇到Enter、Space、Tab鍵。
當cin>>從緩沖區中讀取數據時,若緩沖區中第一個字符是空格、tab或換行這些分隔符時,cin>>會將其忽略並清除,繼續讀取下一個字符,若緩沖區為空,則繼續等待。但是如果讀取成功,字符后面的分隔符是殘留在緩沖區的,cin>>不做處理。
不想略過空白字符,那就使用 noskipws 流控制。比如cin>>noskipws>>input;
#include <iostream> using namespace std; int main() { char a; int b; float c; string cin>>a>>b>>c; cout<<a<<" "<<b<<" "<<c<<" "<<endl; system("pause"); return 0; }
在屏幕中一次輸入:a[回車]11[回車]5.56[回車],程序將輸出如下結果:
a 11 5.56 a 11 5.56
2. cin.get
該函數有有多種重載形式,分為四種格式:無參,一參數,二參數,三個參數。常用的的函數原型如下:
int cin.get(); istream& cin.get(char& var); istream& get ( char* s, streamsize n ); istream& get ( char* s, streamsize n, char delim )。
其中streamsize 在VC++中被定義為long long型。另外,還有兩個重載形式不怎么使用,就不詳述了,函數原型如下:
istream& get ( streambuf& sb); istream& get ( streambuf& sb, char delim );
cin.get讀取一個字符(前兩種格式)
讀取一個字符,可以使用cin.get或者cin.get(var),示例代碼如下:
#include <iostream> using namespace std; int main() { char a; char b; a=cin.get(); cin.get(b); cout<<a<<b<<endl; system("pause"); return 0; }
輸入:e[回車],輸出:
(1)從結果可以看出,cin.get()從輸入緩沖區讀取單個字符時不忽略分隔符,直接將其讀取,就出現了如上情況,將換行符讀入變量b,輸出時換行兩次,一次是變量b,一次是endl。
(2)cin.get()的返回值是int類型,成功則返回讀取字符的ASCII碼值,遇到文件結束符時,返回EOF,即-1。Windows下命令行輸入文件結束符的方式為Ctrl+z,Linux為Ctrl+d。
(3)cin.get(char var)如果成功返回的是cin對象,因此可以支持鏈式操作,如cin.get(b).get(c)。
注:可以利用cin.get()刪除緩沖區遺留下來的換行符。
2.2.2 cin.get讀取一行(后兩種形式)
讀取一行可以使用istream& get ( char* s, streamsize n )或者istream& get ( char* s, size_t n, streamsize delim )。二者的區別是前者默認以換行符結束,后者可指定結束符。n表示目標空間的大小。示例代碼如下:
#include <iostream> using namespace std; int main() { char a; char array[20]={NULL}; cin.get(array,20); cin.get(a); cout<<array<<" "<<(int)a<<endl; system("pause"); return 0; }
輸入:123456789[回車],輸出:
123456789 123456789 10
(1)從結果可以看出,cin.get(array,20);讀取一行時,遇到換行符時結束讀取,但是不對換行符進行處理,換行符仍然殘留在輸入緩沖區。
第二次由cin.get()將換行符讀入變量b,打印輸入換行符的ASCII碼值為10。這也是cin.get()讀取一行與使用getline讀取一行的區別所在。getline讀取一行字符時,默認遇到’\n’時終止,並且將’\n’直接從輸入緩沖區中刪除掉,不會影響下面的輸入處理。
(2)cin.get(str,size);讀取一行時,只能將字符串讀入C風格的字符串中,即char*,但是C++的getline函數可以將字符串讀入C++風格的字符串中,即string類型。鑒於getline較cin.get()的這兩種優點,建議使用getline進行行的讀取。
2.3 cin.getline讀取一行
函數作用:從標准輸入設備鍵盤讀取一串字符串,並以指定的結束符結束。
函數原型有兩個:
istream& getline(char* s, streamsize count); //默認以換行符結束 istream& getline(char* s, streamsize count, char delim);
使用示例:
#include <iostream> using namespace std; int main() { char array[20]={NULL}; cin.getline(array,20); //或者指定結束符,使用下面一行 //cin.getline(array,20,'\n'); cout<<array<<endl; system("pause"); return 0; }
注意,cin.getline與cin.get的區別是,cin.getline不會將結束符或者換行符殘留在輸入緩沖區中。
3. cin的條件狀態
使用cin讀取鍵盤輸入時,難免發生錯誤,一旦出錯,cin將設置條件狀態(condition state)。條件狀態標識符號為:
goodbit:無錯誤 eofbit:已到達文件尾 failbit:非致命的輸入/輸出錯誤,可挽回 badbit:致命的輸入/輸出錯誤,無法挽回
與這些條件狀態對應的就是設置、讀取和判斷條件狀態的流對象的成員函數。他們主要有:
s.eof():若流s的eofbit置位,則返回true;
s.fail():若流s的failbit置位,則返回true;
s.bad():若流s的badbit置位,則返回true;
s.good():若流s的goodbit置位,則返回true;
s.clear(flags):清空狀態標志位,並將給定的標志位flags置為1,返回void。
s.setstate(flags):根據給定的flags條件狀態標志位,將流s中對應的條件狀態位置為1,返回void。
s.rdstate():返回流s的當前條件狀態,返回值類型為strm::iostate。strm::iostate是與機器相關的整型類型,由ios_base類定義,用於定義條件狀態。
了解以上關於輸入流的條件狀態與相關操作函數,下面看一個因輸入緩沖區未讀取完造成的條件狀態位failbit被置位,再通過clear()復位的例子。
#include <iostream> using namespace std; int main() { char ch, str[20]; cin.getline(str, 5); cout<<"flag1:"<<cin.good()<<endl; // 查看goodbit狀態,即是否有異常 cin.clear(); // 清除錯誤標志 cout<<"flag1:"<<cin.good()<<endl; // 清除標志后再查看異常狀態 cin>>ch; cout<<"str:"<<str<<endl; cout<<"ch:"<<ch<<endl; system("pause"); return 0; }
輸入:12345[回車],輸出結果為:
12345 flag1:0 flag1:1 str:1234 ch:5
可以看出,因輸入緩沖區未讀取完造成輸入異常,通過clear()可以清除輸入流對象cin的異常狀態。,不影響后面的cin>>ch從輸入緩沖區讀取數據。因為cin.getline讀取之后,輸入緩沖區中殘留的字符串是:5[換行],所以cin>>ch將5讀取並存入ch,打印輸入並輸出5。
如果將clear()注釋,cin>>ch;將讀取失敗,ch為空。cin.clear()等同於cin.clear(ios::goodbit);因為cin.clear()的默認參數是ios::goodbit,所以不需顯示傳遞,故而你最常看到的就是:cin.clear()。
4. cin清空輸入緩沖區
從上文中可以看出,上一次的輸入操作很有可能是輸入緩沖區中殘留數據,影響下一次的輸入。那么如何解決這個問題呢?自然而然,我們想到了在進行輸入時,對輸入緩沖區進行清空和狀態條件的復位。條件狀態的復位使用clear(),清空輸入緩沖區應該使用:
函數原型:istream &ignore( streamsize num=1, int delim=EOF );
函數作用:跳過輸入流中n個字符,或在遇到指定的終止字符時提前結束(此時跳過包括終止字符在內的若干字符)。
使用示例如下:
#include <iostream> using namespace std; int main() { char str1[20]={NULL},str2[20]={NULL}; cin.getline(str1,5); cin.clear(); // 清除錯誤標志 cin.ignore(numeric_limits<std::streamsize>::max(),'\n'); //清除緩沖區的當前行 cin.getline(str2,20); cout<<"str1:"<<str1<<endl; cout<<"str2:"<<str2<<endl; system("pause"); return 0; }
(1)程序中使用cin.ignore清空了輸入緩沖區的當前行,使上次的輸入殘留下的數據沒有影響到下一次的輸入,這就是ignore()函數的主要作用。其中,numeric_limits<std::streamsize>::max()不過是<limits>頭文件定義的流使用的最大值,你也可以用一個足夠大的整數代替它。如果想清空輸入緩沖區,去掉換行符,使用:
cin.ignore(numeric_limits< std::streamsize>::max(),'\n');清除cin里所有內容。
(2)cin.ignore();當輸入緩沖區沒有數據時,也會阻塞等待數據的到來。
(3)有個疑問,網上很多資料說調用cin.sync()即可清空輸入緩沖區,本人測試了一下,VC++可以,但是在Linux下使用GNU C++卻不行,無奈之下,Linux下就選擇了cin.ignore()。
5.從標准輸入讀取一行字符串的其它方法
5.1 getline讀取一行
C++中定義了一個在std名字空間的全局函數getline,因為這個getline函數的參數使用了string字符串,所以聲明在了<string>頭文件中了。
getline利用cin可以從標准輸入設備鍵盤讀取一行,當遇到如下三種情況會結束讀操作:
(1)文件結束;
(2)遇到行分隔符;
(3)輸入達到最大限度。
函數原型有兩個重載形式:
istream& getline ( istream& is, string& str); //默認以換行符\n分隔行 istream& getline ( istream& is, string& str, char delim);
使用示例:
#include <string> #include <iostream> using namespace std; int main() { string str; getline(cin,str); cout<<str<<endl; system("pause"); return 0; }
輸入:hello world[回車],輸出:
hello world
hello world
注意,getline()遇到結束符時,會將結束符一並讀入指定的string中,再將結束符替換為空字符。因此,進行從鍵盤讀取一行字符時,建議使用getline,較為安全。但是,最好還是要進行標准輸入的安全檢查,提高程序容錯能力。
cin.getline()與getline()類似,但是因為cin.getline()的輸出是char*,getline()的輸出是string,所以cin.getline()屬於istream流,而getline()屬於string流,二者是不一樣的函數。
*5.2 gets讀取一行
gets是C中的庫函數,在<stdio.h>申明,從標准輸入設備讀字符串,可以無限讀取,不會判斷上限,以回車結束或者EOF時停止讀取,所以程序員應該確保buffer的空間足夠大,以便在執行讀操作時不發生溢出。
函數原型:char *gets( char *buffer );
使用示例:
#include <iostream> using namespace std; int main() { char array[20]={NULL}; gets(array); cout<<array<<endl; system("pause"); return 0; }
不建議在C++里面使用;
參考文獻: