深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()
結論總結: C 1.scanf:讀字元:scanf("%c",c);連同回車,空格,tab一起讀進來了,遇到回車結束,可與循環一起讀字元串用(因為能夠讀空格) 2.getchar():和scanf一樣 3.scanf:讀字元串:scanf("%s",s):空格回車tab作用相同,遇到就停止,且丢棄空格回車tab,不能用于讀有空格的字元串 4.gets():讀字元串:可以有空格,且丢棄回車,這才是讀字元串最好的 C++ 5.cin:空格回車tab一樣的效果(之是以隻有輸入回車才結束輸入,是因為輸入回車才将字元串插入讀緩存,cin是直接從讀緩存讀取資料) 且丢棄回車空格tab,不能用于讀字元串,可與循環合用讀一行輸入多個整數存放到buf中 也可以對string進行操作,同樣遇到回車空格tab結束,要想可以輸入空格需要用getline 6.cin.get()/cin.get(c):讀字元與scanf,getchar()相同功能 7.cin.getline(buf,size):讀字元串,與gets()相同功能 8.getline(cin,str):是C++ string中的,因為cin無法輸入空格(空白),進而有了getline,讀取一行,回車結束,回車也讀取,隻是丢棄了沒放到string中去,傳回流參數,如果輸入一開始就 是換行符,則string為空,VC6的此函數有bug,需要按兩個回車才會結束,祥看http://www.cnblogs.com/EnCaL/archive/2012/12/03/2800138.html 注意1:getline()還有個問題就是在cin後的時候,讀取到了cin的回車進而給string賦空值了,使用時候應該這樣cin>>n;cin.ignore();getline(cin,str); cin.ignore()//丢棄cin輸入時候的回車 注意2:經常會遇到輸入一個不定長度的數組或者字元之類的,string的優點之一也就是無需定義長度,但沒有長度什麼時候輸入結束也是一個問題,下面将講解下 第一種:事先輸入一個長度可以動态開辟記憶體,相當于也是定長度了 int n; cin>>n; int *a=new int[n]; for(int i=0;i<n;i++) cin>>a[i]; 第二種:數組的問題用vector,徹底無需指定長度 #include<vector> vector<int> vec_int; int a; while(cin>>a) vec_int.push_back(a); 注意:這裡如果想結束需要按結束标志符win中按ctrl+z然後回車 linux按ctrl+d回車 第三種:字元串,同樣事先輸入個數 int n; cin>>n; char *a=new char[n]; cin.getline(a,n); 第四種:字元串,用string string str getline(cin.str); 第五種:字元串數組 vector<string> vec_str; string str; while(getline(cin,str)) vec_str.push_abck(str); 注意:這裡如果想結束需要按結束标志符win中按ctrl+z然後回車 linux按ctrl+d回車
----------------------------------------------------
| 問題描述一:(分析scanf()和getchar()讀取字元) |
----------------------------------------------------
scanf(), getchar()等都是标準輸入函數,一般人都會覺得這幾個函數非常簡單,沒什麼特殊的。但是有時候卻就是因為使用這些函數除了問題,卻找不出其中的原因。下面先看一個很簡單的程式:
程式1:
#include <stdio.h>
int main()
{
char ch1, ch2;
scanf("%c", &ch1);
scanf("%c", &ch2);
printf("%d %d\n", ch1, ch2);
return 0;
}
或者是:
#include <stdio.h>
int main()
{
char ch1, ch2;
ch1 = getchar();
ch2 = getchar();
printf("%d %d\n", ch1, ch2);
return 0;
}
程式的本意很簡單,就是從鍵盤讀入兩個字元,然後列印出這兩個字元的ASCII碼值。可是執行程式後會發現除了問題:當從鍵盤輸入一個字元後,就列印出了結果,根本就沒有輸入第二個字元程式就結束了。例如使用者輸入字元'a', 列印結果是97,10。這是為什麼呢?
【分析】:
首先我們呢看一下輸入操作的原理, 程式的輸入都建有一個緩沖區,即輸入緩沖區。一次輸入過程是這樣的,當一次鍵盤輸入結束時會将輸入的資料存入輸入緩沖區,而cin函數直接從輸入緩沖區中取資料。正因為cin函數是直接從緩沖區取資料的,是以有時候當緩沖區中有殘留資料時,cin函數會直接取得這些殘留資料而不會請求鍵盤輸入,這就是例子中為什麼會出現輸入語句失效的原因!
其實這裡的10恰好是回車符!這是因為scanf()和getchar()函數是從輸入流緩沖區中讀取值的,而并非從鍵盤(也就是終端)緩沖區讀取。而讀取時遇到回車(\n)而結束的,這個\n會一起讀入輸入流緩沖區的,是以第一次接受輸入時取走字元後會留下字元\n,這樣第二次的讀入函數直接從緩沖區中把\n取走了,顯然讀取成功了,是以不會再從終端讀取!這就是為什麼這個程式隻執行了一次輸入操作就結束的原因!
----------------------------------------------------
| 問題描述二:(分析scanf()和gets()讀取字元串) |
----------------------------------------------------
首先我們看一下scanf()讀取字元串的問題:
程式2:
#include <stdio.h>
int main()
{
char str1[20], str2[20];
scanf("%s",str1);
printf("%s\n",str1);
scanf("%s",str2);
printf("%s\n",str2);
return 0;
}
程式的功能是讀入一個字元串輸出,在讀入一個字元串輸出。可我們會發現輸入的字元串中不能出現空格,例如: 測試一輸入:
Hello world! 輸出:
Hello
world!
【分析】到此程式執行完畢,不會執行第二次的讀取操作!這個問題的原因跟問題一類似,第一次輸入Hello world!後,字元串Hello world!都會被讀到輸入緩沖區中,而scanf()函數取資料是遇到回車、空格、TAB就會停止,也就是第一個scanf()會取出"Hello",而"world!"還在緩沖區中,這樣第二個scanf會直接取出這些資料,而不會等待從終端輸入。
測試二:
Hello[Enter]
Hello[輸出]
world[Enter]
world[輸出]
【分析】程式執行了兩次從鍵盤讀入字元串,說明第一次輸入結束時的回車符被丢棄!即:scanf()讀取字元串會舍棄最後的回車符!
我們再看一下gets()讀取字元串的情況:
用scanf來讀取一個字元串時,字元串中是不可以出現空格的,一旦出現空格,後面的資料就會舍棄殘留在緩沖區中。其實有另外一個函數是可以接受空格的,那就是gets(),下面我們看一下這個函數的應用,我們把程式2改動一下:
程式3:
#include <stdio.h>
int main()
{
char str1[20], str2[20];
gets(str1);
printf("%s\n",str1);
gets(str2);
printf("%s\n",str2);
return 0; }
測試:
Hello world! [輸入]
Hello world! [輸出]
12345 [輸入]
12345 [輸出]
【分析】顯然與上一個程式的執行情況不同,這次程式執行了兩次從鍵盤的讀入,而且第一個字元串取了Hello world! 接受了空格符,而沒有像上一個程式那樣分成了兩個字元串!是以如果要讀入一個帶空格符的字元串時因該用gets(), 而不宜用scanf()!
--------------------------------------------------------
| 問題描述三:(getchar()暫停程式,檢視程式執行結果)|
--------------------------------------------------------
不知道大家有沒有遇到過這樣的問題,有的編譯器程式執行完後的結果界面不會停下而是一閃就沒了,以至于看不到執行結果。是以很多人在程式最後加上getchar()語句,目的是想讓程式執行完後停下來,等待從終端接收一個字元再結束程式。可是發現有時候這樣根本沒用,程式照樣跳出去了。這是為什麼呢?
【分析】原因跟上面例子講的一樣,是因為輸入緩沖區中還有資料,是以getchar()會成果讀到資料,是以就跳出了!
------------------
| 【總結】 |
------------------
第一:要注意不同的函數是否接受空格符、是否舍棄最後的回車符的問題! 讀取字元時:
scanf()以Space、Enter、Tab結束一次輸入,不會舍棄最後的回車符(即回車符會殘留在緩沖區中);
getchar()以Enter結束輸入,也不會舍棄最後的回車符;
讀取字元串時:
scanf()以Space、Enter、Tab結束一次輸入
gets()以Enter結束輸入(空格不結束),接受空格,會舍棄最後的回車符! 第二:為了避免出現上述問題,必須要清空緩沖區的殘留資料,可以用以下的方法解決:
方法1:C語言裡提供了函數清空緩沖區,隻要在讀資料之前先清空緩沖區就沒問題了!
這個函數是fflush(stdin)。
方法2:自己取出緩沖區裡的殘留資料。
(說實話這個語句我也沒看懂,呵呵!為什麼格式控制是這樣的!希望高手指點一下!)
scanf("%[^\n]",string);
二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理
cin<<, cin.get,cin.getline等函數深入分析
很多初學者都認為cin函數是一個很簡單的函數,其實不然!cin函數有很多需要了解的知識(比如:cin的傳回值是什麼,cin提供了哪些成員函數且分别是什麼作用,如cin.clear(), cin.ignore(), cin.fail(), cin.good()等等),如果沒有很好的掌握,在使用的時候很可能會出問題卻不知其原因!而且很多人也确确實實遇到過不少問題,以下是幾個簡單的例子: 程式1:
#include <iostream>
using namespace std;
int main()
{
int m, n;
cin>>m;
cin>>n;
return 0;
}
如果使用者每次都輸入兩個合法的數,程式不會出問題!
但是如果使用者第一次輸入時給一個非法的輸入,比如說輸入一個字元'a',你會發現程式不 會再執行第二條輸入語句。似乎有點奇怪!!
程式2:
#include <iostream>
using namespace std;
int main()
{
char str[8];
cin.getline(str, 5);
cout<<str<<endl;
cin.getline(str, 5);
cout<<str<<endl;
return 0;
}
程式的功能很簡單,就是輸入一個字元串再輸出,再次輸入一個字元串輸出。程式執行情況:
abcd (回車)
abcd (輸出)
efgh (回車)
efgh (輸出)
當使用者第一次輸入的字元串字元數小于4時,程式執行正常!
abcdefgh (回車)
abcd (輸出)
(輸出-換行)
當使用者第一次輸入的字元數字元數大于4時,第一個字元串接受輸入的前四個字元,而第二次的輸入操作沒有執行,第二個字元串輸出為空。似乎也很奇怪!!!
其實在很多時候都會遇到諸如此類的問題,如果不熟悉程式輸入的原理和cin等一些函數的原理就不知道怎麼解決!我在這裡做一個簡單的介紹,也許介紹得不是很準确和全面,或者存在一些誤解,請大家包涵! 輸入操作的原理
與前一節中提到的scanf函數一樣,程式的輸入都建有一個緩沖區,即輸入緩沖區。一次輸入過程是這樣的,當一次鍵盤輸入結束時會将輸入的資料存入輸入緩沖區,而cin函數直接從輸入緩沖區中取資料。正因為cin函數是直接從緩沖區取資料的,是以有時候當緩沖區中有殘留資料時,cin函數會直接取得這些殘留資料而不會請求鍵盤輸入,這就是例子中為什麼會出現輸入語句失效的原因!
cin的一些輸入函數和操作符
cin is a extern istream object。提供了很多可用的成員函數和重載的操作符,如:cin<<, cin.get(), cin.getline()等。下面我們來了解一下這幾個函數:
一. cin<<
該操作符是根據後面變量的類型讀取資料。
輸入結束條件 :遇到Enter、Space、Tab鍵。(這個很重要!)
對結束符的處理 :丢棄緩沖區中使得輸入結束的結束符(Enter、Space、Tab)
讀字元的情況:
程式3:
#include <iostream>
using namespace std;
int main()
{
char c1, c2;
cin>>c1;
cin>>c2;
cout<<c1<<" "<<c2<<endl;
return 0;
}
a[Enter]b[Enter]
a b
第5/9頁
a b[Enter]
a b
讀字元串的情況:
程式4:
#include <iostream>
using namespace std;
int main()
{
char str1[10], str2[10];
cin>>str1;
cin>>str2;
cout<<str1<<endl;
cout<<str2<<endl;
return 0;
}
abcd[Enter]
efgh[Enter]
abcd
efgh
【分析】輸入遇到回車符結束,很正常。
abcd efgh
abcdefgh
【分析】第一次讀取字元串時遇到空格則停止了,将abcd讀入str1,并舍棄了空格,将後面的字元串給了第二個字元串。這證明了cin讀入資料遇到空格結束;并且丢棄空格符;緩沖區有殘留資料室,讀入操作直接從緩沖區中取資料。
二.cin.get()
該函數有三種格式:無參,一參數,二參數
即cin.get(), cin.get(char ch), cin.get(array_name, Arsize)
讀取字元的情況:
輸入結束條件:Enter鍵
對結束符處理:不丢棄緩沖區中的Enter
cin.get() 與 cin.get(char ch)用于讀取字元,他們的使用是相似的,
即:ch=cin.get() 與 cin.get(ch)是等價的。
程式5:
#include <iostream>
using namespace std;
int main()
{
char c1, c2;
cin.get(c1);
cin.get(c2);
cout<<c1<<" "<<c2<<endl; // 列印兩個字元
cout<<(int)c1<<" "<<(int)c2<<endl; // 列印這兩個字元的ASCII值
return 0;
}
a[Enter]
a
97 10
【分析】會發現隻執行了一次從鍵盤輸入,顯然第一個字元變量取的'a', 第二個變量取的是Enter(ASCII值為10),這是因為該函數不丢棄上次輸入結束時的Enter字元,是以第一次輸入結束時緩沖區中殘留的是上次輸入結束時的Enter字元!
a b[Enter]
a
97 32
【分析】顯然第一個字元變量取的'a', 第二個變量取的是Space(ASCII值為32)。原因同上,沒有丢棄Space字元。
讀取字元串的情況:
cin.get(array_name, Arsize)是用來讀取字元串的,可以接受空格字元,遇到Enter結束輸入,按照長度(Arsize)讀取字元, 會丢棄最後的Enter字元。
程式6:
#include <iostream>
using namespace std;
int main ()
{
char a[20];
cin.get(a, 10);
cout<<a<<endl;
return 0;
}
abc def[Enter]
abc def
【分析】說明該函數輸入字元串時可以接受空格。
第7/9頁
1234567890[Enter]
123456789
【分析】輸入超長,則按需要的長度取資料。
程式7:
#include <iostream>
using namespace std;
int main ()
{
char ch, a[20];
cin.get(a, 5);
cin>>ch;
cout<<a<<endl;
cout<<(int)ch<<endl;
return 0;
}
12345[Enter]
1234
53
【分析】第一次輸入超長,字元串按長度取了"1234",而'5'仍殘留在緩沖區中,是以第二次輸入字元沒有從鍵盤讀入,而是直接取了'5',是以列印的ASCII值是53('5'的ASCII值)。 測試二輸入:
1234[Enter]
a[Enter] 輸出:
1234
97
【分析】第二次輸入有效,說明該函數把第一次輸入後的Enter丢棄了!
三.cin.getline()
cin.getline() 與 cin.get(array_name, Arsize)的讀取方式差不多,以Enter結束,可以接受空格字元。按照長度(Arsize)讀取字元, 會丢棄最後的Enter字元。
但是這兩個函數是有差別的:
cin.get(array_name, Arsize)當輸入的字元串超長時,不會引起cin函數的錯誤,後面的cin操作會繼續執行,隻是直接從緩沖區中取資料。但是cin.getline()當輸入超長時,會引起cin函數的錯誤,後面的cin操作将不再執行。(具體原因将在下一部分"cin的錯誤處理"中詳細介紹)
程式8:
#include <iostream>
using namespace std;
int main ()
{
char ch, a[20];
cin.getline(a, 5);
cin>>ch;
cout<<a<<endl;
cout<<(int)ch<<endl;
return 0;
}
12345[Enter]
1234
-52
【分析】與cin.get(array_name, Arsize)的例程比較會發現,這裡的ch并沒有讀取緩沖區中的5,而是傳回了-52,這裡其實cin>>ch語句沒有執行,是因為cin出錯了!