字符串的輸入輸出


本文部分內容參考了C Primer Plus(Fifth Edition)

C語言字符串表示

  字符串是C語言中最常用也是最重要的數據類型,但是C語言沒有專門提供這種類型。因為字符串由字符組成,所以聲明字符串,我們用字符數組。字符數組是字符串的變量表示方法。純字符數組和字符串的區別和聯系就是:字符串是一個以'\0'結尾的字符數組。因此,我們聲明一個字符數組char ch[32]實際上它最多只能存儲31個可顯示字符,最后一個字符是'\0',它是字符串結尾的標志。

  字符串還有一種表示方法,那就是字符串常量(字符串字面量)。例如printf("%s","King and Queen");這個表達式語句中"King and Queen"就是字符串常量。實際上,它也是一個元素為字符常量的數組,這個數組內容為(char []){'K','i','n','g',' ','a','n','d',' ','Q','u','e','e','n','\0'};千萬注意別忘了'\0'。

  因為字符串常量如"Anytime"可以表示這個字符串(准確說是元素為字符常量的數組)的首地址。因此,我們可以用指針來操作字符串。我們可以這樣聲名:

char * chptr = "Anytime";

但不能這樣:

char * chptr;
*chptr = "Anytime";

因為*chptr表示chptr所指向的地址上面的內容,如果這個指針未初始化,那么這就是個很危險的操作,指針有可能亂指向內存空間,如果指向的是系統文件,它就會修改系統文件。即使指針已經初始化,我們也不應該這樣做,原因有兩個:

  1.這樣做可能導致溢出(超出了安全的內存空間)

  2.會更改不應該更改的內容(詳見這篇文章,它解釋得很好)

因此,指針操作字符串常量不安全,一般只用來傳輸字符串變量的地址(內容為字符變量的數組)。

字符串基本輸入

  scanf()雖然有專門的%s來輸入字符串,但它的終止條件是遇到如空格,換行符等空白字符。不如說它是用來處理單詞輸入的。而處理長字符串輸入,最早是用gets(),當它讀到換行符時丟棄'\n'並結束輸入,它很好用(對於很早以前來說),但我不希望大家掌握它,盡管很多隨便的程序都在用它。

char cha[5];
gets(cha);

這個函數真的非常好用,直接把數組首地址代進去就行了。你可否注意到一個嚴重的缺陷?這個函數不知道這個數組的大小,也就是不知道它最多只能接受多少字符的輸入,這就會導致溢出!有一些UNIX系統的代碼大量使用了gets(),使得黑客有機會通過這個漏洞編寫程序將垃圾數據寫入系統,導致系統癱瘓,這就是流行於這些UNIX計算機之間的蠕蟲病毒。

  我推薦大家使用fgets()函數,這個函數使用起來比gets()安全,但更加麻煩。

 

char * cha[16];
fgets(cha, 16, stdin); //fgets(名稱,大小,讀取文件)
//如果要用這個函數從鍵盤讀取,請在讀取文件的地方用上stdin

 

傳入的大小為n,它就最多讀取n - 1個字符或遇到換行符時終止。例如,上面的代碼運行后我輸入"1234567890123456"后,cha = "123456789012345"。這個函數看起來很完美,但十全十美的東西是不存在的。fgets()的缺陷在於它讀到換行符時保存了換行符!下面是C Primer Plus第五版的有關程序示例:

  此時,我們很迫切想編寫一個函數,讓它丟棄fgets()保存的換行符及后面的無效字符。如果不丟棄無效字符,就會導致后面的語句誤讀了緩沖區,就像初學字符輸入時輸入一個字符回車后再要輸入一個字符,可還沒有輸入就已經執行到后面去了。我們把我們自己編寫的函數取名為s_gets(),我們讓它的返回值和fgets()的返回值一樣。我們編寫的函數代碼如下:

 1 char * s_gets(char * sptr, int size){
 2     int i = 0; //i表示讀取項數
 3     char * re; //re返回和fgets()一樣的數值
 4     re = fgets(sptr,size,stdin);
 5     if(re){ //如果re != NULL
 6         while( (sptr[i] != '\n') && (sptr[i] != '\0') ) //讀取sptr[i]直到讀到'\0'或'\n'
 7             i++;
 8         if(sptr[i] == '\n') //如果讀到的是'\n'
 9             sptr[i] = '\0'; //把它變成'\0'
10         else
11             while( ( getchar() ) != '\n' ) //如果讀到的是別的東西,一直讀到'\n',防止后面的語句誤讀
12                 continue;
13     }
14     return re;
15 }

  如果日后需要進行安全的字符串輸入,用這個函數就OK了。運用這個函數的示例運行:

 字符串基本輸出

  printf()函數提供了一個接近於完美的字符串輸出,而且,它還可以直接輸出數字(如%d,%u等)。printf()函數的通用性很強,因此,如果不想使用其它的字符串輸出函數,一定要記住這個。相信即使剛學C的初學者也知道這個函數的用法。

  puts()函數簡潔易用,直接給出字符串的地址就行了。需要注意的是,puts()函數在字符串輸出后會加上'\n',所以puts()和gets()以及上面我們自己寫的s_gets()配套使用。

char cha[16] = "Memory";
puts(cha);

  fputs()函數主要和fgets()配套,需要提醒的是,這兩個函數不僅可以用在標准I/O上,還可以進行文件處理,而且一般是用在文件處理上的。fputs()函數需要兩個參數,第一個參數給出字符串地址,第二個參數給出輸出位置。因為它和fgets()配套使用,所以它在輸出字符串之后不加上'\n'。

char * cha[16] = "Memory\n";
fputs(cha,stdin);

  下面給出C Primer Plus第五版中有關的例程:

自己編寫自定義的字符串輸入輸出函數!

  你也可以自己編寫你自己的輸入輸出函數,而且,假設你編寫的函數沒有什么太大的錯誤,這些函數比上面所提到的大部分函數都更加可靠和靈活。我們可以使用getchar()和putchar()來完成字符串的輸入輸入功能。

  下面是我寫的函數,讀者在讀完后也應該自己動手寫寫,說不定以后還能用上呢!

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 #define SIZE 16
 5 
 6 char * ud_gets(char * st, int size){ //輸入
 7     int i = 0; //計數器
 8     while( ( ( st[i] = getchar() ) != '\n') && (i < size - 1) )
 9         i++;//如果讀到非'\n'字符或未超出限定范圍,則繼續讀取
10     while( (i > size - 1) && (getchar() != '\n'))
11         continue;//如果讀取超過限定范圍且后面還有字符,則丟棄
12     st[i] = '\0'; //結束讀取
13     return st;
14 }
15 
16 char * ud_puts(char * st, int ad_enter){ //輸出
17     int i = 0;
18     while(putchar(st[i]) != '\0') //如果沒有讀到字符串結尾就繼續讀
19         i++;
20     if(ad_enter) //如果加上'\n'的開關被打開
21         putchar('\n'); //輸出換行符
22     return st;
23 }
24 
25 int main(int argc, char * argv[]){
26     char cha[SIZE];
27     ud_gets(cha,SIZE);
28     ud_puts(cha,0);
29     getch();
30     return 0;
31 }

  運行結果:

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM