C语言 替换gets函数

目录

    • 替换gets函数
      • gets()用处
      • gets()的危险之处
      • gets()的几种替代方法
        • 一、用%c循环输入直到遇到换行结束
        • 二、用getchar()循环输入直到遇到换行结束
        • 三、scanf的另一种用法
        • 四、c++中的getline()方法
        • 五、解决方案使用fgets代替

替换gets函数

gets()用处

gets从标准输入设备读字符串函数,其可以无限读取,不会判断上限,可以包含空格,以回车结束读取。

gets()的危险之处

因为该函数可以无限读取,所以应该确保buffer的空间足够大,以便在执行读操作时不发生溢出。如果溢出,多出来的字符将被写入到堆栈中,这就覆盖了堆栈原先的内容,破坏一个或多个不相关变量的值。这个事实导致gets函数只适用于玩具程序。

gets()的几种替代方法

既然gets()的用处是用来读取一个包含空格的字符串,那么我们就有了以下几种方法来代替gets():

一、用%c循环输入直到遇到换行结束

#include
#include
#include
#include
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;
}

我们可以用上面的方法来读取一个包含空格的字符串,但是实际操作中遇到了下面的情况:

C语言 替换gets函数_第1张图片

从图片中可以看出,我们给str输入的是“123 456 789”,但是输出结果却并不是我们想要的,这是为什么呢?

答案很简单,当我们输出str字符串的时候,系统是以’\0’符号来判断一个字符串的末尾的,我们输入遇到’\n’的时候就跳出循环了,所以后面的内容是不可预知的,直到遇到’\0’才停止输出。

要怎样解决呢?

更简单了,既然字符串需要以’\0’结束,那我们只需要把字符串的末尾的那个字符手动赋值为’\0’即可:

#include
#include
#include
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;
}

这个时候我们再来验证一下,发现问题就解决了:

C语言 替换gets函数_第2张图片

二、用getchar()循环输入直到遇到换行结束

这个方法从原理是跟上面的方法是一样的,只是写法不一样,下面直接放上参考代码:(值得注意的是末尾仍要赋成’\0’)

#include
#include
#include
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 
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

并且str必须定义为string类型

string str;

需要注意的是,既然str定义的是string类型,则说明求字符串长度函数strlen()将不再可用,C++提供了另一种方法:

int len = str.size();

下面来验证一下:

C语言 替换gets函数_第3张图片

五、解决方案使用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);

你可能感兴趣的:(C语言积累,c语言,算法,开发语言)