目录
- 替换gets函数
- gets()用处
- gets()的危险之处
- gets()的几种替代方法
- 一、用%c循环输入直到遇到换行结束
- 二、用getchar()循环输入直到遇到换行结束
- 三、scanf的另一种用法
- 四、c++中的getline()方法
- 五、解决方案使用fgets代替
替换gets函数
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();
下面来验证一下:
五、解决方案使用fgets代替
fgets(temp,sizeof(temp),stdin);
gets
已被弃用,因为它很危险,可能会导致缓冲区溢出。
解决方案
//接收用户输入,gets函数已经被弃用,这里替换成fgets函数,由于fgets函数会读入回车,这里将回车去掉
fgets(msg.data, sizeof(msg.data), stdin);
//printf("%s", msg.data);
msg.data[strlen(msg.data) - 1] = '\0';
//printf("-------------\n");
//printf("%s", msg.data);