使用C语言中的fgets函数计算字符串长度

4

我有一个问题。我尝试使用fgets函数后查看一些字符串的长度。如果我输入的字符串长度小于可以包含在字符串中的字母数(例如:字符串中的最大字母数为9,我输入4个字母),则会得到字符串长度+1的结果。为什么?

这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char name[10]={0};
    printf("enter your name\n");
    fgets(name, 10, stdin);
    printf("your name is %s and it is %d letters\n", name, strlen(name)); // length problem 

    return 0;
} 

1
fgets 的 man-page 有什么不清楚的地方吗?你在调试器中检查了函数后的数组吗?你发现字符串末尾有什么东西? - too honest for this site
2
fgets() 包括输入中的换行符 — 如果它读取了换行符。 - Jonathan Leffler
1
strlen 返回 size_t,因此请使用 %zu 作为格式说明符。 - LPs
仔细查看你的程序输出,你会发现在输入的四个字符后面打印了一个换行符。这个换行符是第五个元素:\n - alk
我输入了4个字母 --> 提示:你输入了5个按键。 - chux - Reinstate Monica
显示剩余2条评论
4个回答

5

来自fgets手册页面(https://linux.die.net/man/3/fgets):

fgets()最多从流中读取比大小少一个字符并将其存储到指向s的缓冲区中。在EOF或换行符之后停止阅读。如果读取到换行符,则将其存储到缓冲区中。终止空字节(aq\0aq)存储在缓冲区中的最后一个字符之后。

因此,在您的4个字母后面添加了'\n',返回string_length + 1

删除fgets()输入中的尾随换行符,您可以将@Tim Čas的解决方案添加到您的代码中。

该行仍使用fgets()函数读取,然后我们删除换行符。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(void)
{
    char name[10] = { 0 };
    printf("enter your name\n");
    fgets(name, 10, stdin);
    printf("your name is %s and it is %d letters\n", name, strlen(name)); // length problem 
    name[strcspn(name, "\n")] = 0;
    printf("NEW - your name is %s and it is %d letters\n", name, strlen(name));
    return 0;
}

这将输出:

enter your name
Andy
your name is Andy
 and it is 5 letters
NEW - your name is Andy and it is 4 letters
Press any key to continue . . .

那么如何获取字符串而不带有它添加的换行符(如果函数添加了\n,则删除它)? - Raz Omry
我已经更新了答案。 - jgorostegui
查看 @LPs %zu - chux - Reinstate Monica

3
如果字符数组有足够的空间,那么标准函数fgets也会在数组中包含换行符'\n',通常对应于按下的Enter键。
您可以通过以下方式删除此多余的换行符。
name[strcspn( name, "\n" )] = '\0';

之后,您将获得应用函数strlen的预期结果。


2
由于fgets()函数将包含末尾换行符'\n'的字符串复制到name中。

1
正如man fgets中所写,

fgets()函数从给定的流中最多读取比size少一个字符的数量,并将它们存储在字符串str中。当找到换行符、文件结束或错误时,阅读停止。如果读取了任何字符且没有错误,则附加`\0'字符以结束该字符串。

由于您正在从stdin读取,fgets(name, 10, stdin)从stdin缓冲区中最多读取9个字符,并在末尾添加\0。恰好发生的是,用户按回车键时产生的新行字符\n也在缓冲区中。

顺便说一下,当指定传递给fgets的数组的大小时,使用sizeof()是惯例(也是一种良好的实践)。

fgets(name, (int) sizeof(name), stdin);

1
“它永远不会出错!”虽然这是机器,但仍然可能被错误使用。 想象一下,如果将指针用作数组,将会发生什么: char * readstr(char * p) { return fgets(p, sizeof p, stdin); } 在大多数系统上,将尝试读取4或8个char字符。 - alk
“它永远不会出错!” --> 请注意,sizeof返回类型为size_t,但是fgets(char * s, int n, FILE * stream)使用int作为大小。对于巨大的数组,这可能会意外转换。虽然很少见,但并非绝对不会发生。 - chux - Reinstate Monica
如果你不检查返回值,怎么知道它永远不会出错呢?fgetsstdin读取时,如果遇到EOF,就会返回NULL - RoadRunner
我只是建议OP在fgets中传递sizeof(name)而不是10。当事情脱离上下文时,它常常会出错。感谢评论。已更新回答。 - Pejman
@PejmanGh 如果我说话听起来很粗鲁,我很抱歉。我给你点赞 :)。 - RoadRunner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接