fgets详细使用方式及其可能出现的问题与误解(2904字超详细,看了包会的)

一.为什么使用fgets

c语言中常用的输入方式是scanf,但是scanf在解决一些问题的时候具有缺陷,比如在需要判断的字符串中具有空格,那可能就会导致一些问题。因为scanf中空格是默认分隔符,在连续输入的时候一个含有空格的字符串可能就会被当成两个输入,但是我们实际上只要一个输入。而且c11移除了gets(),这下不得不使用fgets了,或者gets_s。

举个栗子:41fb40980c314967972ab0cdc0e2a867.png

5ae1fdea049441a69e38c35f48fdcae3.png

图中第一个字符串就被当成了两个输入,并且空格并没有被统计进去。所以就轮到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 函数,如 fgetsfscanfgetchar 等,来从流中读取数据。

这里不理解也没关系当做格式记下来就好了。

还是刚才的例子

改完后变为

while (fgets(str,sizeof(str),stdin))

0946e0d5ac664c49b816ea22a80c4dd4.png顺带一提红框标出来的是fgets的连续输入格式,如果加上~会报错,!= EOF也不好,下文注意事项会讲。

我们来看运行结果:49f5984bcc1d40cdb6fa041ff38ebeaf.png

不对啊为什么空格多一个呢,细心的你可能发现了我使用了isspace这个函数,那我就顺带一块讲了,isspace这个函数会计算传入的值是否为空格,制表符,换行符,如果为上述值则返回非零值。需要引用头文件#include<ctype.h>

1.制表符:这里我们首先排除制表符,因为制表符一般是按tab用来快捷输入空格对齐用的,你输入字符串为什么会用到tab键对吧,而且就算按了tab也会显示空格。

2.也不是因为手滑在末尾打了空格,所以排除空格

所以那么就只剩一种情况就是换行符了,还记得前面我标红的字体吗,fgets是用来读取整行的内容,那么也会将‘\n’也就是回车enter键也读取,所以最后需要将space_count - 1。

d208379c83914550900a6f73e82e8186.png

然后结果就对辣!

代码:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值