gets()用處
gets從標準輸入設備讀字符串函數,其可以無限讀取,不會判斷上限,可以包含空格,以回車結束讀取。
gets()的危險之處
因爲該函數可以無限讀取,所以應該確保buffer的空間足夠大,以便在執行讀操作時不發生溢出。如果溢出,多出來的字符將被寫入到堆棧中,這就覆蓋了堆棧原先的內容,破壞一個或多個不相關變量的值。這個事實導致gets函數只適用於玩具程序。
gets()的幾種替代方法
既然gets()的用處是用來讀取一個包含空格的字符串,那麼我們就有了以下幾種方法來代替gets():
一、用%c循環輸入直到遇到換行結束
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<string>
using namespace std;
int main()
{
char str[100];
char ch;
int i=0;
while(scanf("%c", &ch) && ch != '\n')
{
str[i++] = ch;
}
cout << str << endl;
return 0;
}
我們可以用上面的方法來讀取一個包含空格的字符串,但是實際操作中遇到了下面的情況:
從圖片中可以看出,我們給str輸入的是“123 456 789”,但是輸出結果卻並不是我們想要的,這是爲什麼呢?
答案很簡單,當我們輸出str字符串的時候,系統是以'\0'符號來判斷一個字符串的末尾的,我們輸入遇到'\n'的時候就跳出循環了,所以後面的內容是不可預知的,直到遇到'\0'才停止輸出。
要怎樣解決呢?
更簡單了,既然字符串需要以'\0'結束,那我們只需要把字符串的末尾的那個字符手動賦值爲'\0'即可:
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int main()
{
char str[100];
char ch;
int i=0;
while(scanf("%c", &ch) && ch != '\n')
{
str[i++] = ch;
}
str[i] = '\0'; //手動吧字符串末尾字符賦值成'\0'
cout << str << endl;
return 0;
}
這個時候我們再來驗證一下,發現問題就解決了:
二、用getchar()循環輸入直到遇到換行結束
這個方法從原理是跟上面的方法是一樣的,只是寫法不一樣,下面直接放上參考代碼:(值得注意的是末尾仍要賦成'\0')
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int main()
{
char str[100];
char ch;
int i=0;
while((ch = getchar()) != '\n')
{
str[i++] = ch;
}
str[i] = '\0'; //手動吧字符串末尾字符賦值成'\0'
cout << str << endl;
return 0;
}
三、scanf的另一種用法
我們知道用scanf的%s可以用來輸入一個字符串,但是%s遇到空格之後便停止了,不能達到輸入空格的效果,所以我們可以使用另一種方法:
scanf("%[^\n]%*c", str);
看似很複雜的一句代碼,下面我們來解讀一下:
這句話的意思是碰見了回車就退出,然後把緩衝區裏面的內容按字符串格式輸入str中,回車依然留在緩衝區。
其中"%[^\n]"表示讀入一個字符串,遇到'\n'停止,並設置末尾的'\0'。^ 是“非”的意思,意思就是說把一個非“\n”字符讀入字符串,直到遇到“\n”停止輸入。
而“%*c”呢,則是代表讀入一個字符到緩衝區,但是不向任何地方輸入。這樣,就解決了字符串後邊的“\n”對下面數據的影響,如果不加“%*c”的話,則大多數情況下需要在scanf前加一句getchar()來消除回車的影響。
附:
其實所有對%s起作用的控制都可以用%[],比如%[0-9]表示只讀入'0'到'9'之間的字符,%[a-zA-Z]表示只讀入字母,'-'是範圍連接符,當然也可以直接列出你需要讀入的字符。
如果你只需要讀"abc"裏面的字符就可以用%[abc] (或者%[cab]、%[acb]、%[a-c]、%[c-a].....),
如果想讀入某個範圍之外的字符串就在前面加一個'^',如:%[^a-z]就表示讀入小寫字母之外的字符。
例如從鍵盤輸入的"1235ab86"中讀取1235、86給n,有如下方法:
#include <stdio.h>
bool skip(){
scanf("%*[^0-9]");
return true;
}
void main()
{
int n;
while(skip() && scanf("%d", &n)!=EOF)
printf("%d\n", n);
}
輸出爲:
1235
86
四、c++中的getline()方法
getline不是C庫函數,而是gcc的擴展定義或者C++庫函數。它會生成一個包含一串從輸入流讀入的字符的字符串。
具體用法:
getline(cin, str);
需要注意的是,str字符串必須是C++中的string字符串類型
也就是說必須包含頭文件
#include<string>
並且str必須定義爲string類型
string str;
需要注意的是,既然str定義的是string類型,則說明求字符串長度函數strlen()將不再可用,C++提供了另一種方法:
int len = str.size();
下面來驗證一下:
以上即爲gets()函數的幾種替代方法,如有疑問或者建議,歡迎評論批評指正。
其中,方法三中部分參考了這篇博客。