scanf,gets以及fgets输入字符串区别分析

1.scanf输入字符串解析

直接上代码,如下所示:

  1 #include <stdio.h>
  2 #include <string.h>
  3 int main(int argc, char *argv[])
  4 {
  5         char str[100];
  6 //      gets(str);
  7         scanf("%s",str);
  8         printf("输入的字符串为:\n");
  9         printf("%s\n",str);
 10         //打印字符串最后一个数据(结束符\0前面一个)
 11         printf("最后一个字符为:%c\n",str[strlen(str)-1]);
 12         //输入结束打印后再次读取缓冲区数据
 13         printf("请再次输入\n");
 14         printf("读取打印缓冲区数据:%x\n",getchar());
 15         return 0;
 16 }

只需要输入1次即可得到代码的执行结果,如下图所示:

fly@fly-virtual-machine:~/test$ ./2
11111
输入的字符串为:
11111
最后一个字符为:1
请再次输入
读取打印缓冲区数据:a
fly@fly-virtual-machine:~/test$ 

从上述代码执行结果可以看出,打印最后一个字符‘1’之后,再次使用getchar获取输入缓冲区的内容得到的十六进制结果0x0a,其对应的ASCII表中的‘\r’(关于回车对应的ASCII是\r还是\n等结果请自行百度查看其区别)。从执行结果可以看出,scanf并未将输入缓冲区数据全部读取,当执行到getchar时,因为输入缓冲区存在数据,并不需要我们从键盘再次数据内容,程序即可执行结束。如果我们输入“111+空格+1”时会是怎么样呢?下图是程序执行结果:

111 1
输入的字符串为:
111
最后一个字符为:1
请再次输入
读取打印缓冲区数据:20
fly@fly-virtual-machine:~/test$ 

此时读取到str中的字符串为“111”,执行getchar获取的数据为space对应的ASCII码值,此时输入缓冲区后续还存在字符‘1’和回车,程序未进一步读取测试。若上次输入中的空格改成“111+TAB+1”,执行结果中str任然只有“111”,getchar获取输入缓冲区的数据应该为tab键对应的ASCII码值0x09,程序执行结果如下图所示:

fly@fly-virtual-machine:~/test$ ./2
111	1
输入的字符串为:
111
最后一个字符为:1
请再次输入
读取打印缓冲区数据:9
fly@fly-virtual-machine:~/test$ 

从上述三次测试结果可以看出,sacnf输入时遇到空格,tab或者回车时会终止输入,后续输入内容不会读取到变量中(包含输入结束符空格,tab或者回车),只会存在数据输入缓冲区中等待进一步读取,可以使用getchar,scanf或者gets函数进行获取。

gets输入字符串解析

将上述代码中的scanf输入更改为gets输入,代码如下所示:

  1 #include <stdio.h>
  2 #include <string.h>
  3 int main(int argc, char *argv[])
  4 {
  5         char str[100];
  6         gets(str);
  7 //      scanf("%s",str);
  8         printf("输入的字符串为:\n");
  9         printf("%s\n",str);
 10         //打印字符串最后一个数据(结束符\0前面一个)
 11         printf("最后一个字符为:%c\n",str[strlen(str)-1]);
 12         //输入结束打印后再次读取缓冲区数据
 13         printf("请再次输入\n");
 14         printf("读取打印缓冲区数据:%x\n",getchar());
 15         return 0;
 16 }

执行结果如下图所示:

fly@fly-virtual-machine:~/test$ ./2
11111
最后一个字符为:1
请再次输入

代码运行时,第一次输入“11111”字符串之后,此时终端提示再次输入内容,后续输入‘2’之后执行结果如下图所示:

fly@fly-virtual-machine:~/test$ ./2
11111
最后一个字符为:1
请再次输入
2
读取打印缓冲区数据:32
fly@fly-virtual-machine:~/test$ 

分析上述代码可知,gets函数从输入缓冲区获取输入内容之后,输入缓冲区此时为空,即回车的内容被gets函数读取了,但是并未赋值到str中,当执行到getchar时,需要用户在终端再次从键盘上输入内容。下面展示分别输入“111+空格+1”和“111+TAB+1”的执行结果。
输入“111+空格+1”执行结果:

fly@fly-virtual-machine:~/test$ ./2
111 1
输入的字符串为:
111 1
最后一个字符为:1
请再次输入
2
读取打印缓冲区数据:32
fly@fly-virtual-machine:~/test$ 

输入“111+TAB+1”执行结果:

fly@fly-virtual-machine:~/test$ ./2
111		1
输入的字符串为:
111		1
最后一个字符为:1
请再次输入
2
读取打印缓冲区数据:32
fly@fly-virtual-machine:~/test$ 

分析上述三次测试,gets函数读取到回车结束符后,执行后续的打印函数;当执行到getchar时,此时输入数据缓冲区无内容,所以程序在等待我们从键盘上输入,输入之后程序才会彻底结束。得出如下结论:gets把空格和tab作为字符读取且并不会导致读取结束,遇到回车键时才会停止并且回车键并不会保存在数据缓冲区中。

小结

scanf和gets函数均可实现字符串的输入,但是scanf遇到空格 tab或者回车就会结束输入,结束符以及后续输入内容保存在输入数据缓冲区中;gets输入时,空格和tab键均作为字符读取且不会造成读取结束,gets执行结束后输入数据缓冲区中无输入结束符。但是gets函数使用上存在风险。gets如果没有遇到回车会持续读取数据,设计程序是应保证字符串长度足够大,防止数据溢出造成严重后果。

fgets

上述分析我们得出get存在隐患,因此我们引入新的函数fgets从指定的流中读取数据。Linux C中程序运行时会自动打开stdin,stdout和stderr三个流指针,其分别对应着标准输入,标准输入和标准错误;从终端输入我们则可以利用stdin标准输入流指针结合fgets实现。

char buffer[100] = {0};
fgets(buffer,sizeof(buffer),stdin);
if('\n' == buffer[strlen(buffer)-1])
	 buffer[strlen(buffer)-1] = '\0';
else
	while('\n' != getchar());

此函数需要换行符,或者sizeof(buffer)-1个字符后会自动结束读取,若字符串最后一个字符为‘\n’,则因为终端输入结束(此时我们需要处理掉被读取到字符串中的‘\n’,这样才能和在终端上输入的字符串保持一致);否则因为我们设置读取固定长度导致的读取函数结束,此时我们需要处理掉缓冲器的数据,否则会影响下次我们的操作。代码如下所示:

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
	char buff[10];
	for(int i = 0;i < sizeof(buff);i++)
	{
		printf("%d\t",buff[i]);
	}	
	printf("\n");
	fgets(buff,sizeof(buff),stdin);
	for(int i = 0;i < sizeof(buff);i++)
	{
		printf("%d\t",buff[i]);
	}
	printf("\n");
	for(int i = 0;i < sizeof(buff);i++)
	{
		putchar(buff[i]);
		putchar('\t');
	}
	putchar(10);
	return 0;
}

运行结果(其中10为‘\n’的ASCII码值),可以看出会把‘\n’读取到字符串中切自动帮我们在尾部补上字符串结束标志‘\0’;最后一行乱码是因为程序未对buff进行初始化导致的。

fly@fly-virtual-machine:~/test$ ./1
0	0	48	-59	28	-24	-3	127	0	0	
123
49	50	51	10	0	-24	-3	127	0	0	
1	2	3	
		�	�	

继续上代码,入下图:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
	char buff[10];
	fgets(buff,sizeof(buff),stdin);
	// if('\n' == buff[strlen(buff)-1])
	// 	 buff[strlen(buff)-1] = '\0';
	printf("%s\n",buff);
	return 0;
}

若注释部分未开启,在终端输入123回车后,会出现两个换行符,运行结果如下图所示,因此我们使用fgets进行字符串进行输入时需要处理尾部的‘\n’。

fly@fly-virtual-machine:~/test$ ./1
123
123

fly@fly-virtual-machine:~/test$ 

若fgets不是结束后buff中无‘\n’该如何进行处理呢?在处理的情况下继续调用fgets测试看下结果,代码如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
	char buff[10];
	fgets(buff,sizeof(buff),stdin);
	if('\n' == buff[strlen(buff)-1])
		 buff[strlen(buff)-1] = '\0';
	printf("%s\n",buff);
	fgets(buff,sizeof(buff),stdin);
	if('\n' == buff[strlen(buff)-1])
		 buff[strlen(buff)-1] = '\0';	
	printf("%s\n",buff);	 
	return 0;
}

运行结果如下所示,因为第一个fgtes读取到123456789刚好9个字节,最后一个自动补‘\0’(上面说过最大为sizeof(buff)-1个字节);第一个fget执行完毕之后缓冲区还存在“00\n”三个字节,因此当再次执行到fgets时则自动读出且存在‘\n’会导致fgets自动结束;所以终端只需要输入一次“12345678900\n”即可执行结束,且第二次读取到“00”(因为‘\n’被我们使用程序处理成了‘\0’)。

fly@fly-virtual-machine:~/test$ ./1
12345678900
123456789
00
fly@fly-virtual-machine:~/test$ 

根据上述执行结果如果我们希望第一在终端输入不影响第二个fgets,我们需要将代码修改如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
	char buff[10];
	fgets(buff,sizeof(buff),stdin);
	if('\n' == buff[strlen(buff)-1])
		 buff[strlen(buff)-1] = '\0';
	//新增处理缓冲区剩余部分
	else
		while ('\n' != getchar());
	printf("%s\n",buff);
	
	fgets(buff,sizeof(buff),stdin);
	if('\n' == buff[strlen(buff)-1])
		 buff[strlen(buff)-1] = '\0';	
	printf("%s\n",buff);	 
	return 0;
}

使用getchar()循环读取缓冲区,直至遇到‘\n’,这时缓冲区数据已被我们全部 清除,则不会影响后续的fgets输入。

fly@fly-virtual-machine:~/test$ ./1
12345678900
123456789
   

注意事项

若在fgets前使用scanf进行输入,因为scanf会把‘\n’保留在缓冲区中,因此在使用fgets前需要我们清除下缓冲区,否则fgets会因为scanf输入保留在缓冲区的‘\n’直接读取结束。测试代码如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
	int number;
	char buff[10];
	scanf("%d",&number);
	fgets(buff,sizeof(buff),stdin);
	if('\n' == buff[strlen(buff)-1])
		 buff[strlen(buff)-1] = '\0';
	else
		while ('\n' != getchar());
	printf("%s\n",buff);
	return 0;
}

运行结果如下所示:

fly@fly-virtual-machine:~/test$ ./1
10

fly@fly-virtual-machine:~/test$ 
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值