輸入緩衝區(Input buffer)是用來放置使用者輸入的資料,並由一個pointer來控制read/write的位置
這個pointer在C語言中稱為File pointer,而在C++稱為get pointer。先看個簡單的範例:
::::::::::::::::::::::::::::::::::::::::::::::
#include<stdio.h>
int main()
{
int num;
char chr;
printf("Input one int:");
scanf("%d",&num);
printf("Input a character:");
scanf(" %c",&chr); //注意%c之前有空格
printf("%d\n%c\n",num,chr);
return 0;
}
:::::::::::::::::::::::::::::::::::::::::::::
假設輸入100及A,100和A會被放置到輸入緩衝區中,並且搭配著File pointer控制著:
執行第一個scanf("%d",&num);
(1)num = 100;
(2)File pointer移動到下一個位置:
執行第二個scanf(" %c",&chr);
(1)chr = 'A';
(2)File pointer移動到下一個位置:
目前應該對於輸入緩衝區有點感覺了!接著來從錯誤的輸入資料中研究輸入的機制:
::::::::::::::::::::::::::::::::::::::::::::::
#include<stdio.h>
int main()
{
int num1,num2;
char chr;
printf("Input two ints:");
scanf("%d %d",&num1,&num2);
printf("Input a character:");
scanf("%c",&chr);
printf("%d\n%d\n%c\n",num1,num2,chr);
return 0;
}
:::::::::::::::::::::::::::::::::::::::::::::
輸出(紅色表示輸入的資料):
Input two ints:A 100
Input a character:-858993460
-858993460
A
為什麼輸出的結果是這個樣子呢?說明一下:
(1)將輸入資料'A'與100放置到輸入緩衝區中
(2)執行scanf("%d %d",&num1,&num2);
(3)由於scanf第一個輸入格式是整數(%d),與File pointer所指的資料('A')不符,造成輸入錯誤
使得scanf無法繼續進行輸入的動作,表示scanf("%c",&chr);也無法進行輸入的動作,直到
輸入緩衝區中File pointer指向EOF(End-Of-File)或是清空輸入緩衝區
(4)輸入發生錯誤,使得使用者無法進行程式所指定的輸入,但輸入緩衝區仍會將資料放置到scanf
輸入變數中與輸入緩衝區資料相符合的記憶體中:
(Ⅰ)緩衝區中的資料有'A'與100,而File pointer是指著資料'A'
(Ⅱ)執行完第一個scanf(...),緊接著第二個scanf(...),輸入格式與'A'相符,所以將'A'的
資料複製到chr,File pointer往下一個位置移動
(5)因為num1與num2並沒有從輸入緩衝區中得到符合的資料而且也沒有給定初始值,所以印出來的值
是前次程式執行之所殘留在記憶體內的無效值
如果覺得有點模糊沒有關係,再舉一個範例來說明這個機制:
::::::::::::::::::::::::::::::::::::::::::::::
#include<stdio.h>
int main()
{
int num1,num2;
char chr;
printf("Input two ints:");
scanf("%d",&num1);
scanf("%d",&num2);
printf("Input a character:");
scanf(" %c %d",&chr,&num2);
printf("%d\n%d\n%c\n",num1,num2,chr);
return 0;
}
:::::::::::::::::::::::::::::::::::::::::::::
輸出(紅色表示輸入的資料):
Input two ints:100 A
Input a character:20
100
20
A
(1)將輸入資料100與'A'放置到輸入緩衝區中,File pointer指向資料100的位置
(2)執行第一個scanf("%d",&num1);,輸入格式是整數(%d),與輸入緩衝區內File pointer所
指位置的資料格式相符合,故將100複製給num1,並將File pointer移到下一個位置
(3)執行第二個 scanf("%d",&num2);,輸入格式是整數(%d),與輸入緩衝區入File pointer所指
位置的資料('A')格式不相符合,故輸入產生錯誤,無法執行使用者輸入的動作。
(4)執行第三個 scanf(" %c %d",&chr,&num2);,第一個輸入格式是字元(%c),與輸入緩衝區內
File pointer所指位置的資料('A')格式相符合,故將'A'複製給chr,並將File pointer移到下一個位置
(5)因為緩衝區內File pointer所指的位置已無資料,此時File pointer指向EOF(End-Of-File),所以輸入
的動作復原,scanf(...)可執行使用者輸入的動作,故第三個scanf(...)的第二個輸入格式可以接收使
用者的輸入,所以作者輸入20。
(6)接著將變數內的資料顯示出來。
經過以上的說明相信多多少少能瞭解使用scanf(...),當發生輸入資料格式的錯誤時會發生什麼樣的狀況。
那要如何解決這個問題呢?有兩種方式
(1)把在輸入緩衝區內的資料清除掉,
利用fflush(stdin); or fflush(NULL);
(2)把在輸入緩衝區內的資料消化掉,
char dummy[20]; //數字自己訂!合適就好!
gets(dummy);
在做數值的輸入時,若寫程式的人沒有注意到接收到錯誤的資料時,容易使程式產生錯誤,這點必須注意。
為了避免以上的錯誤,可以先利用字串的方式接收使用者的輸入資料,然後將每個資料做分解的動作,在依
據資料的格式做分配的動作,以下簡單介紹最後一個範例:
::::::::::::::::::::::::::::::::::::::::::::::
#include<stdio.h>
int main()
{
int num1,num2;
char str[81];
printf("Input two ints:");
gets(str);
if(sscanf(str,"%d%d",&num1,&num2)==2)
printf("%d,%d\n",num1,num2);
else
printf("Input error!!!");
return 0;
}
::::::::::::::::::::::::::::::::::::::::::::
sscanf()會傳回多少個 fields被有效scan,可以判斷輸入是否符合格式。
接下來介紹容易有問題的程式:
::::::::::::::::::::::::::::::::::::::::::::
#include<stdio.h>
#include<stdlib.h>
int main()
{
int num;
char ch;
while(1)
{
printf("Please input number:");
scanf("%d",&num);
printf("Continue...(Y/N)?");
scanf("%c",&ch);
if( tolower(ch)=='n')
break;
}
return 0;
}
:::::::::::::::::::::::::::::::::::::::::::: 執行結果(藍色字為輸入的部份):
Please input number:1
Continue...(Y/N)?Please input number:n
Continue...(Y/N)?
問題來了,為什麼會是這樣的顯示結果呢?
因為在輸入1的同時,按下Enter鍵之後,真正在輸入緩衝區中的資料是數值1及'\r'(Enter鍵的ASCII代碼)
1被num變數給接收了!而接下來的'\r'是character,所以直接給ch做為輸入資料
這就導致為什麼scanf("%c",&ch);沒有在畫面中產生作用的原因。
等'\r'被接收之後,迴圈再執行一次,而輸入'n'時,因為與scanf("%d",&num);中整數格式不符合
所以不做接收的動作,等到第二個scanf("%c",&ch);,'n'是字元的型態,所以儲存至ch變數中。
也就因此離開迴圈!
解決辦法,有兩種:
(1)在"%c"中,%c前面空一格==>" %c"
(2)在scanf("%d",&num);底下加上fflush(stdin);
那為什麼(1)中加空格就行了呢?
加了空格表示接收非空格型態的資料,空格型態包括Enter鍵、Tab鍵、空白鍵
可以參考書籍的介紹!
有二個不錯的的函式可以學學,在此推薦給各位:
sscanf();
sprintf();
Written By James On 2003/10/07