只是为了确认我所想的事情是否真的发生了。当我将字母表(26 个字母)作为输入时,以下代码会打印出 25,这是因为 fgets 总是自动将 n 个元素数组中的第 n 个元素设置为 ‘\n’ 吗?
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[26];
printf("String: ");
fgets(str, 26, stdin);
printf("%lu\n", strlen(str));
}
这样,当我尝试打印字母表的 strlen 时,它会在 ‘\n’ 之前停止并返回 25?
2
最佳答案
3
如果整行适合缓冲区,根据您的大小参数(26
在您的情况下),其中包括字符串空终止符,fgets
则会添加换行符。
如果缓冲区无法容纳整行,则fgets
不会添加换行符,只会添加缓冲区可以容纳的内容(包括空终止符)。
因此,如果您输入超过 200 个25
字符(换行符除外),那么长度将始终为 200 25
。
另外,您应该始终检查返回的内容。
该函数返回正确格式为类型的值。size_t
%zu
如果可能的话,sizeof
在使用时使用fgets
。如fgets(str, sizeof str, stdin)
。
2
-
+1 表示换行符。你没有忘记,终端的 26 个符号条目也包含换行符,因此我们得到 26+1 或 2+1 = 29 个符号数组
– -
4“然后 fgets 将添加换行符。” –> 更像是“然后 fgets 将读取并保存
'z'
换行符。”
–
|
OP 输入了:"abcdefghijklmnopqrstuvwxyz"
Enter。共有 27 个字符:'a'
到 的字符'z'
以及换行符'\n'
。
fgets(str, 26, stdin);
读取最多25 个字符stdin
,然后附加一个空字符, '\0'
。
因此strlen(str)
是 25 并"z\n"
留在stdin
稍后阅读。
而是读入更大的缓冲区并砍掉潜在的尾随'\n'
。
// Large enough for 26 letters, \n, appended \0
char str[ 26 +1 +1];
printf("String: ");
if (fgets(str, sizeof str, stdin)) {
printf("%zu\n", strlen(str)); // Expect 27. Note specifier changed to %zu.
str[strcspn(str, "\n")] = '\0'; // Lop off potential trailing '\n'.
printf("%zu\n", strlen(str)); // Expect 26
}
|
你正在朝着正确的方向思考。
您可以查看文档(大多数情况下使用 cpp 文档是可以的):
它指出,签名是:
char* fgets(char* str, int count, FILE* stream);
文档说:
从给定的文件流中读取最多 count – 1 个字符并将它们存储在 str 指向的字符数组中。
因此,您给它一个 26 个元素的数组,它会用最多 25 个元素填充它并附加终止符。在 C 中,终止符是一个零字节 ( '\0'
)。
strlen
实际上,它会在给定地址后搜索下一个终止符。它会找到该终止符str[25] == '\0'
并返回 25 作为结果。
在某些应用程序中,该位置存在很大的安全问题 –如果您计划将其用作以空字符结尾的 C 字符串,则应始终char*
在字符数组中为终止符添加一个额外的项。否则,所有函数都不会在字符串末尾停止处理 – 它们仅对指针进行操作,并且len
大多数情况下没有给出参数。
总是这样吗?
在某些情况下,空字符不用作终止符,甚至可以作为数据的一部分,但在这种情况下,您不应将此数组称为“字符串”。在某些协议中,您会在其他地方列出字符数组的长度。例如,请参阅 BER-TLV。它包含连接的标签号、长度和数组本身。以下是标签 0x55 中包含的“123”的示例:
55 03 31 32 33
31 32 33
– ASCII 中的 3 个字节,代表“123”。请注意 – 没有空终止符!
虽然这种格式在通信中被广泛使用,但是您不能在strlen
或中使用指向此类数组的指针作为参数printf
。
感谢@some-programmer-dude。我必须提一下:
当您在终端中输入某个字符串时,您会用回车符(换行符)终止该输入。在 Linux 中它只是"\n"
(1 个字节),而在 Windows 中它是"\r\n"
(2 个字节)。
假设您输入John
并按下 Enter。流将包含:
"John\n"
– 即 5 个字节。但要存储此文本并将其用于printf
的%s
替换或例如 ,strlen
您还应该存储一个浮动空终止符。因此您可能需要分配至少 6 个字节:
str[6] = {'J', 'o', 'h', 'n', '\n', '\0'};
当您调用 strlen 时,它将返回自给定指针以来非零元素的数量 – 即 5。如果您随后重用该数组并存储,例如仅存储“?”,那么您将获得:
str[6] = {'?', '\n', '\0', 'n', '\n', '\0'};
注意,strlen
将返回 2,而在 之后数组不会被清除\0
。所有函数都将忽略第一个检测到的字符之后的数据'\0'
,而 printf(例如)将仅“看到” "?\n"
。
|
fgets()
不会“自动将第 n 个元素设置为”。它会\n
从输入中读取所有字符,直到给定的缓冲区大小或直到(包括)换行符为止。同样,fgets()
不会“添加换行符”。它只是读取输入。如果输入不包含换行符,则缓冲区也不包含换行符。–
fgets
或者说根本不是fgets
。–
|