11.2.1 建立存儲空間編程
要作的第一件事是創建一個空間以存放讀入的字符串。數組
最簡單的辦法就是在聲明中明確指出數組的大小:函數
char name[81] ;工具
如今的name是一個已經分配81字節存儲塊的地址。另外一個方法是使用C庫裏分配存儲空間的函數,這一點會在第12章討論。設計
爲字符串預留空間後,就能夠讀取字符串了。C庫提供了三個讀取字符串的函數:scanf( )、gets( )和fgets( )。咱們先討論最經常使用的gets( )。指針
11.2.2 gets( ) 函數code
它從系統的標準輸入設備(一般是鍵盤)獲取一個字符串。由於字符串沒有預約的長度,因此gets( )須要知道什麼時候結束。解決辦法是讀字符串直到遇到一個換行字符(\n),按回車鍵能夠產生這個字符。它讀取換行符(不包括換行符)以前的全部字符,在這些字符後添加一個空字符(\o),而後把這個字符串交給調用它的程序。它將讀取換行符並將其丟棄,這樣下一次讀取就會在新一行開始。對象
程序清單11.4 name1.c程序內存
/*name1.c 讀取一個名字*/ #include<stdio.h> #define MAX 81 int main(void) { char name[MAX]; /*分配空間*/ printf("Hi,what's your name?\n"); gets(name); /*把字符串放進name數組*/ printf("Nice name ,%s.\n",name); return 0; }
程序清單11.4接受並存儲最多80個字符(包括空格)的任何名字(記住爲數組裏的\o預留空間)。注意到但願gets( )改變調用函數中的某個變量(name),也就是說應當使用一個地址做爲參數;固然,數組名正是一個地址。字符串
get( )函數的使用能夠比前的例子更爲複雜,請參見程序清單11.5.
程序清單11.5 name2.c程序
/*name1.c 讀取一個名字*/ #include<stdio.h> #define MAX 81 int main(void) { char name[MAX]; /*分配空間*/ char * ptr; printf("Hi,what's your name?\n"); ptr=gets(name); /*把字符串放進name數組*/ printf("Nice name, %s.\n",ptr); return 0; }
get( )函數經過兩種方式獲取輸入:
**它使用一個地址,把字符串賦給name;
**gets( )的代碼使用return關鍵字返回字符串的地址,程序把這個地址分配給ptr。注意到ptr是一個char的指針,這意味着gets必須返回一個指向char的指針值。
ANSI C要求stdio.h頭文件包括gets( )的函數原型。您不須要親自聲明這個函數,只須記住包含這個頭文件便可。gets( )函數的構造以下:
char *gets(char *s) { ... return (s); }
這個函數頭說明gets( )返回一個指向char的指針。請注意,gets( )返回的指針與傳遞給它的是同一個指針。輸入字符串只有一個備份,它放在做爲函數參數傳遞過來的地址,所以程序清單11.5中的ptr最後也指向name。gets( )函數實際的構造更復雜一點,由於它有兩個可能的返回值:若是一切都順利,它返回的是讀入字符串的地址,正如上面所說的。若是出錯或gets( )遇到文件結尾,它就返回一個空(0)地址。這個空地址被稱爲空指針,並用stdio.h中定義的常量NULL來表示。所以,gets( )中還加入了一些錯誤檢測,這使它能夠很方便的使用以下形式使用:
while (gets(name)!=NULL)
這個的指令使您便可以檢查是否到了文件的結尾,又能夠讀取一個值。若是遇到了文件的結尾,name中什麼也不會讀入。這種一箭雙鵰的方法就比getchar( )函數所採用的方法簡潔的多,getchar( )只返回一個值而沒有參數。
while ((ch=getchar())!=EOF)
附帶提一下,不要混淆空指針和空字符。空指針是一個地址,而空字符是一個char類型的數據對象,其值爲0.數值上二者均可以用0表示,可是它們的概念不一樣:NULL是一個指針,而0是一個char類型的常量。
11.2.3 fgets( )函數
gets( )的一個不足是它不檢查預留存儲區是否可以容納實際輸入的數據。多出來的字符簡單地溢出到相鄰的內存區。fgets( )函數改進了這個問題,它讓您指定最大讀入字符數。因爲fgets( )是爲文件I/O設計的,在處理鍵盤輸入時就不如gets()那麼方便。fgets()和gets()有三方面不一樣:
一、它須要第二個參數來講明最大讀入字符數。若是這個參數值爲n,那麼fgets( )就會讀取最多n-1個字符或者讀完一個換行符爲止,由這兩者中最早知足的那個來結束輸入。
二、若是fgets( )讀取到換行符,就會把它存放在字符串裏,而不是像gets()那樣丟棄;
三、它還須要第三個參數來講明讀哪一個文件。從鍵盤上讀取輸入時,可使用stdin做爲該參數,這個標識符在stdio.h中定義。
程序清單11.6使用了fgets( )代替了11.5裏的gets( )。
程序清單11.6 name3.c 程序
/*name1.c 讀取一個名字*/ #include<stdio.h> #define MAX 81 int main(void) { char name[MAX]; /*分配空間*/ char * ptr; printf("Hi,what's your name?\n"); ptr=fgets(name,MAX,stdin); /*把字符串放進name數組*/ printf("Nice name, %s.\n",ptr); return 0; }
問題在於fgets( )把換行符存儲到字符串裏,這樣每次顯示字符串時就會顯示換行符。本章後面「其餘字符串函數」小節的結尾會介紹如何用strchr( )來定位和刪除換行符。
對於重要的編程,應當使用fgets( )而不是gets( )。
11.2.4 scanf( )函數
scanf( ) 和get( )的主要差異在於它們如何決定字符串什麼時候結束。
scanf()更基於如何獲取單詞(get word)而不是獲取字符串(get string);而gets( )函數,正如您所看到的,會讀取全部字符,直到遇到第一個換行符爲止。
scanf( )使用兩種方法來決定輸入結束。不管哪一種方法,字符串都是以遇到第一個非空白字符開始。若是使用%s格式,字符串讀到下一個空白字符(但不包括)(好比空格、製表符和換行符)。若是指定了字段的寬度,好比%10s,scanf( )就會讀入10個字符或直到遇到第一個空白字符,由兩者中最早知足的那一個終止輸入。
程序清單11.7舉例說明了指定字段寬度時scanf( )的工做狀況。
/*scan_str.c 使用scanf()*/ #include<stdio.h> int main(void) { char name1[11],name2[11]; /*分配空間*/ int count; printf("Please inter 2 names.\n"); count=scanf("%5s %10s",name1,name2); printf("I read the %d names %s and %s.\n", count,name1,name2); return 0; }
第三個例子輸出:
Please enter 2 names . portensia callowit I read 2 names Porte and nsia.
portensia的後4個字母被讀到name2中,這是由於第二次調用scanf( )時,它在第一個調用 結束的地方繼續開始讀取輸入數據。
根據所需輸入的特色,用gets()從鍵盤讀取文本可能要更好。scanf()主要用於以某種標準形式輸入的混合類型數據的讀取和轉換。例如,每個輸入行都包括一種工具名稱、庫存數量和單價,您就可使用scanf();不然您必須在函數中本身處理輸入錯誤的檢測。若是但願一次只輸入一個單詞,最好使用scanf( )。