一.为什么使用fgets
c语言中常用的输入方式是scanf,但是scanf在解决一些问题的时候具有缺陷,比如在需要判断的字符串中具有空格,那可能就会导致一些问题。因为scanf中空格是默认分隔符,在连续输入的时候一个含有空格的字符串可能就会被当成两个输入,但是我们实际上只要一个输入。而且c11移除了gets(),这下不得不使用fgets了,或者gets_s。
举个栗子:
图中第一个字符串就被当成了两个输入,并且空格并没有被统计进去。所以就轮到fgets登场了。
代码:
#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main()
{
char str[50] = { 0 };
while (~scanf("%s",str))
{
int space_count = 0;
int alpha_count = 0;
int digit_count = 0;
int other_count = 0;
int len = strlen(str);//只到\0,\0和后面的\n不会不会被计入
int i = 0;
for (i = 0; i < len; i++)
{
if (isspace(str[i]))
{
space_count++;
}
else if (isalpha(str[i]))
{
alpha_count++;
}
else if (isdigit(str[i]))
{
digit_count++;
}
else
{
other_count++;
}
}
printf("%d %d %d %d\n", space_count, alpha_count, digit_count, other_count);
}
return 0;
}
二.fgets到底该怎么使用
fgets用于读取整行的内容
fgets的格式是:fgets(str,int n,stdin);
和其他输入输出函数一样需要引用头文件#include<stdio.h>
str是你要输入的字符串
int n是你要读取的最大字符数,因为你要读取整个字符串所以一般写成sizeof(str),至于为什么不是strlen后文会有详细解释。
stdin
是 C 语言标准库中的一个宏定义,它代表标准输入流(Standard Input Stream)。在大多数操作系统中,标准输入流默认连接到键盘,允许程序从键盘读取用户的输入。
在 C 语言中,stdin
被定义为指向 FILE
类型的指针,它是标准 I/O 库中定义的一种数据结构,用于表示打开的文件或流。FILE
类型的指针可以用于调用各种标准 I/O 函数,如 fgets
、fscanf
、getchar
等,来从流中读取数据。
这里不理解也没关系当做格式记下来就好了。
还是刚才的例子
改完后变为
while (fgets(str,sizeof(str),stdin))
顺带一提红框标出来的是fgets的连续输入格式,如果加上~会报错,!= EOF也不好,下文注意事项会讲。
我们来看运行结果:
不对啊为什么空格多一个呢,细心的你可能发现了我使用了isspace这个函数,那我就顺带一块讲了,isspace这个函数会计算传入的值是否为空格,制表符,换行符,如果为上述值则返回非零值。需要引用头文件#include<ctype.h>
1.制表符:这里我们首先排除制表符,因为制表符一般是按tab用来快捷输入空格对齐用的,你输入字符串为什么会用到tab键对吧,而且就算按了tab也会显示空格。
2.也不是因为手滑在末尾打了空格,所以排除空格
所以那么就只剩一种情况就是换行符了,还记得前面我标红的字体吗,fgets是用来读取整行的内容,那么也会将‘\n’也就是回车enter键也读取,所以最后需要将space_count - 1。
然后结果就对辣!
代码:
#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main()
{
char str[50] = { 0 };
while (fgets(str,sizeof(str),stdin))
{
int space_count = 0;
int alpha_count = 0;
int digit_count = 0;
int other_count = 0;
int len = strlen(str);//只到\0,\0和后面的\n不会不会被计入
int i = 0;
for (i = 0; i < len; i++)
{
if (isspace(str[i]))
{
space_count++;
}
else if (isalpha(str[i]))
{
alpha_count++;
}
else if (isdigit(str[i]))
{
digit_count++;
}
else
{
other_count++;
}
}
printf("%d %d %d %d\n", space_count-1, alpha_count, digit_count, other_count);
}
return 0;
}
三.注意事项
1.fgets什么时候会停止读取
fgets(str,int n,stdin)中int n代表的是可读取最大字符数
1.遇到\n也就是回车
2.当 fgets
读取到 n - 1
个字符时,它会停止读取,以确保不会超出目标缓冲区的边界。这是出于安全考虑,以防止缓冲区溢出,这是一种严重的安全漏洞,可能导致程序崩溃或被恶意利用。
如果我初始化char str[5] = {0}, fgets输入hello,使用sizeof(str)结果就是5,也就是说我读取到5-1个字符'h' 'e' 'l' 'l' 就停止了,最后补上'\0'使得字符串合法;如果读取n个字符,也就是5个字符,‘h’ ‘e’ ‘l’ ‘l’ ‘o’,为了让字符串合法最后一定会补上'\0',那么就会非法访问空间,造成2中说的问题。
3.遇到EOF(所以为什么说连续输入时!=EOF不好,是因为多余了)
2.为什么是sizeof而不是strlen
有人会想,如果我用strlen,初始化char str[50] = {0}(防止越界),fgets(str,strlen(str),stdin);我输入一个hello,strlen只会读取\0以前的长度,也就是hello\n,最后加上'\0'为7,不是也行吗。但是实际上一个字符都不会读取,因为执行顺序是这样的,先执行int n也就是strlen(str),由于str数组为空,strlen(str)长度实际为零。然后再读取字符,所以hello一个字符都不读取。
3.输入端
char str[50] = {0};fgets(str,sizeof(str),stdin);你输入hello的时候会加上回车也就是hello\n为了确保字符串合法会在最后加上\0也就是hello\n\0,有人可能会想成我输入了hello这个字符串那么就是hello\0最后加上回车\n,最后是hello\0\n,但是这个输入本质上是输入一个字符串,放\0一定是最后的动作,hello\0\n\0这种情况一定是不存在的,因为这种情况太怪了,设计的时候肯定会避免。
4.使用fgets后的字符串打印问题
我们通过上文已知fgets读取时会把'\n'也读进去,所以最后打印的时候要小心,防止出现连续两个'\n'
四.结尾语
最后本篇书写的时候本来以为会很快结束,但还是肝到了凌晨,1800字的文章加上凌晨书写难免会存在纰漏,希望大家能够帮忙指正改进,最后文章很长感谢你能够耐心读到这里,我们下次再见,886。