天天看點

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

深入了解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;

}

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

如果使用者每次都輸入兩個合法的數,程式不會出問題!

但是如果使用者第一次輸入時給一個非法的輸入,比如說輸入一個字元'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;
}
           

程式的功能很簡單,就是輸入一個字元串再輸出,再次輸入一個字元串輸出。程式執行情況:

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

abcd (回車)

abcd (輸出)

efgh (回車)

efgh (輸出)

當使用者第一次輸入的字元串字元數小于4時,程式執行正常!

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

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;
}
           
深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

a[Enter]b[Enter]

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

a b

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

第5/9頁

a b[Enter]

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

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;
}
           
深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

abcd[Enter]

efgh[Enter]

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

abcd

efgh

【分析】輸入遇到回車符結束,很正常。

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

abcd efgh

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

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;
}
           
深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

a[Enter]

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

a

97 10

【分析】會發現隻執行了一次從鍵盤輸入,顯然第一個字元變量取的'a', 第二個變量取的是Enter(ASCII值為10),這是因為該函數不丢棄上次輸入結束時的Enter字元,是以第一次輸入結束時緩沖區中殘留的是上次輸入結束時的Enter字元!

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

a b[Enter]

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

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;
}
           
深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

abc def[Enter]

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

abc def

【分析】說明該函數輸入字元串時可以接受空格。

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

第7/9頁

1234567890[Enter]

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

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;
}
           
深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

12345[Enter]

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

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;
}
           
深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

12345[Enter]

深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()深入了解scanf()/getchar()/gets()/cin/cin.get/cin.getline()/getline()二:C/C++學習筆記2 - cin深入分析(上) - cin輸入操作處理

1234

-52

【分析】與cin.get(array_name, Arsize)的例程比較會發現,這裡的ch并沒有讀取緩沖區中的5,而是傳回了-52,這裡其實cin>>ch語句沒有執行,是因為cin出錯了!