很多在做ACM题的时候经常会被一些字符串输入搞糊涂。 如果你认为几个字符串输入函数的功能基本相同,那就大错特错了。 今天就深入分析一下C语言中的三个字符串输入函数(scanf、gets、fgets),免得翻了太多ACMer字符串输入的阴沟。 (仅讨论命令行上的标准输入)
让我们首先讨论最常见的 scanf 输入字符串。 众所周知,使用scanf("%s")输入字符串的一个显着特点就是字符串中不能有空格、回车、制表符等空白字符。 因为scanf遇到空白字符就结束字符串输入。 请看下面这段代码:
### N 20
int main()
字符str[N];
scanf("%s", 字符串);
("%s\n", 字符串);
0;
编译运行后输入“abc def”会发生什么? 程序的结果是什么?下面是我的测试结果
这说明什么? scanf 确实无法读取带有空格字符的字符串。 因此,当需要读取带有空白字符的字符串时,使用scanf是一个不明智的选择。
接下来讲一下scanf函数的一个非常重要的特性:使用scanf函数作为命令行的标准输入,只要以换行符(即回车键)结束输入就可以了使用scanf读取单个字符,那么这个换行符就会被放入输入缓冲区中。 如果您的下一次输入是在输入字符或使用gets等函数输入字符串时,您的下一次输入可能会受到影响。 接下来做一个实验,请看这段代码:
### N 20
int main()
字符str[N];
字符ch;
scanf("%s", 字符串);
("%s\n", 字符串);
ch = ();
("%d\n", ch);
0;
编译运行后,输入“abc”,回车结束输入。 以下是我的测试结果
我使用字符变量ch来接受后续的字符输入,然后打印该字符的ASCII码值。 该字符的ASCII码值为10,正是换行符的ASCII码。 仅仅用scanf输入字符串是不够的,我们应该尝试使用scanf函数输入其他比如整数浮点数来尝试,这里我就不一一写代码进行实验了。 我试了一下,结果还是一样:输出ch的ASCII码值仍然是10。这意味着scanf函数在输入以a结尾时,会将换行符留在输入缓冲区中,而不是清除换行符。换行符。当然我想说的是,这种情况下,scanf后面没有换行符,请看这段代码
### N 20
int main()
字符str[N];
字符ch;
scanf("%s", 字符串);
("%s\n", 字符串);
scanf("%c", &ch);
("%d\n", ch);
ch = (); //读取第二次scanf后剩余的字符,实际上没有
("%d\n", ch);
0;
运行后,使用与上面相同的输入,结果仍然是与上面相同的输出。
但不同的是,程序结束,而是再次等待输入,因为第一次scanf输入后留下的换行符被第二次scanf读取,而换行符之后没有后续输入,所以函数在等待输入。 解释:并不是scanf生成换行符放入输入缓冲区,而是用户输入的换行符没有被scanf吃掉,从而保留在缓冲区中。
接下来是 gets 函数。 当我们需要读取包含空白字符的字符串时,基本上就是 gets 函数。 gets 函数恰好是专门为读取带有空白字符的字符串而设计的。 事实上,gets 函数会一直读取直到换行符停止。 测试代码如下:
### N 20
int main()
字符str[N];
字符ch;
获取(str);
("%s\n", 字符串);
("%c\n", str[(str)-1]); //输出字符串str的最后一个字符,不是'\0'
ch = ();
("%d\n", ch);
0;
当我输入字符串“I am a”并按回车键结束输入时,结果是这样的
ch字符正在等待输入,这意味着gets函数的输入字符串不会像scanf函数那样在输入缓冲区中留下换行符。 那么这个换行符是否被读入字符串中呢? 不! 因为输出字符串的最后一个字符发现这个字符不是换行符而是字母't',而这恰好是我输入字符串的最后一个字符。 可以看出,gets函数会吃掉换行符,但不会将其放入字符串中,这相当于在scanf函数中添加了一个可以读取不是换行符的空白字符的函数。
接下来我们看一下scanf和gets交替使用读取字符串的现象。 测试代码如下:
### N 20
int main()
字符str1[N];
字符 str2[N];
字符ch;
scanf("%s", str1);
获取(str2);
("str1=%s;\n", str1);
("str2=%s;\n", str2);
ch = ();
("ch = %d\n", ch);
0;
我打算先输入“”,然后按回车键输入另一个字符串,但是当我输入完“”并回车时,第二个字符串已经被输入了,现象如下:
显然,等待输入的是一个函数,字符串str2是一个空字符串(即直接是字符串终止符'\0'的字符串,长度为0)。 我们不妨根据之前得到的结论解释一下为什么会这样:scanf函数在输入缓冲区中留下一个换行符,并被gets函数读取,因为gets函数遇到换行符就结束字符串的输入,而换行符之前的字符已经被scanf函数读到了,所以gets函数读到的是一个空字符串,而gets函数会吃掉换行符,不会将其放入字符串中,所以当前函数还在等待用于输入状态。 其实你不妨做一个实验,当你用gets函数输入一个字符串,只按回车键,那么在gets函数中读到的就是一个空字符串。 一个明显的结论是,scanf函数不可能读取空字符串,因为它不会读取空白字符,所以它会一直等待输入,直到出现非空白字符。
接下来我们要讲的是第三个字符串输入函数fgets。 fgets本来是一个文件输入函数,但也可以用于标准输入。 它相对于gets函数的优点是可以限制字符串输入的长度,避免缓冲区溢出。 ,很多网络攻击都是缓冲区溢出攻击。 换句话说,fgets函数比gets函数更安全。 我们仍然使用之前的测试代码,只是将gets替换为fgets,代码如下:
### N 20
int main()
字符str1[N];
字符 str2[N];
字符ch;
scanf("%s", str1);
fgets(str2, (str2), 标准输入);
("str1=%s;\n", str1);
("str2=%s;\n", str2);
ch = ();
("ch = %d\n", ch);
0;
输入仍然完全相同。 输入“”并回车后,fgets就完成了输入。 由此可见,fgets和gets的功能还是很相似的,但是有一处不同。 看看我的结果:
显然,函数仍在等待输入,但字符串str2与之前不同。 应该说fgets函数是在字符串中添加一个换行符。 str2不是一个空字符串,而是一个只有一个换行符的字符串。 字符串,如果输出str2的长度你会发现它的长度为1。fgets和gets在输入字符串中除了限制输入字符串的长度之外的主要区别在于fgets会读取换行符到字符串中像 gets 函数一样破坏换行符。
至此,这三个C语言字符串输入函数的分析比较就结束了。 如果有人发现任何其他谜团,,,别忘了分享,,