fgets()在末尾包含换行符

13
fgets(input,sizeof(input),stdin);
if (strcmp(input, "quit") == 0){
  exit(-1);
}

如果我输入 quit,程序并没有退出;我想知道这是为什么。

顺便提一下,input 被声明为 char *input;


1
如果您真的声明了 char *input; 而没有为其分配指向空间,那么您很幸运(或者我应该说不幸?)没有出现崩溃。您必须确保有足够的空间:char input[64]; 更好;除非有令人信服的理由使用更短的长度,否则我通常使用4096作为行长。此外,您应该检查 fgets() 是否读取了一些数据:if (fgets(input, sizeof(input), stdin) != 0) ... OK - read some data... - Jonathan Leffler
我感到困惑,这篇文章的标题意味着你已经知道问题出在哪里了,你改过标题吗? - flarn2006
@flarn2006 只需检查编辑历史记录。有人更改了它。 - underscore_d
5个回答

13

fgets函数可能会在读取字符串的末尾添加一个换行符。 您需要检查它:

函数fgets有时会在读取完一行后自动加上一个换行符(\n),如果你不想要这个换行符,你需要手动去掉。

size_t ln = strlen(input) - 1;
if (input[ln] == '\n')
    input[ln] = '\0';

甚至更多

strtok(input, "\n");

好像在printf时已经有了换行符。如何去掉这个换行符,或者改变strcmp函数让其考虑到这一点呢? - Man Person
谢谢,使用了Olaf的解决方案因为它更简单一些。但另一个也可以。 - Man Person
1
fgets() 不会添加换行符;它将包括标记行末的换行符。这样,您就可以知道是否已读取整行。 - Jonathan Leffler
3
如果第一个读取的字符是空字符,ln 的值就会变成 SIZE_MAX,从而导致 input[ln] 的越界读取 --> UB。为避免这种漏洞,可以检测 ln 或使用 ln[strcspn(ln,"\n")] = 0;。如果第一个字符是 '\n'strtok(input, "\n"); 就无法去掉该字符。 - chux - Reinstate Monica

8
在您的输入中存在尾随换行符。请参见 man fgets。例如,测试“quit”+换行符:
fgets(input,sizeof(input),stdin);
if(strcmp(input, "quit\n") == 0){
    exit(-1);
}

我完全错过了最后一句话,关于 char *input。根据架构不同,input 的长度将为4或8个字节。因此,代码实际上是这样的:

fgets(input, 8, stdin);

这并不反映内存中input指向的真实大小。只要输入长度小于8字节,这可能会“正常工作”,但如果输入更大,则会截断输入。此外,下次调用fgets时,您将获得其余输入。
您应该提供实际大小或采用@JonathanLeffler的建议并声明一个char数组,例如:
char input[64];
fgets(input, sizeof(input), stdin);

或者
char *input = malloc(N);
fgets(input, N, stdin);

4
很遗憾,这并不总是有效的。比如你将输入声明为char input[5],然后输入quit。此时fgets不会在末尾添加换行符。因此,请始终检查是否已添加换行符。 - codaddict
只是为了让我的观点清晰明确:请比较以下两个链接的输出结果:http://ideone.com/3vs6Ed 和 http://ideone.com/MVfvy6。 - codaddict

0
建议您将代码编写为:

if(strstr(input, "quit") != NULL){

原因:这将解决人们添加额外字符的问题(例如,在文本前后添加空格)。


-1

这个解决方案只需要标准库(stdio.h),并且可以得到相同的结果。

for (i = 0; input[i] != '\0'; i++); /* getting the string size */
input[i-1] = '\0'; /* removing the newline */

-2
我所做的是将换行符替换为 '\0' 空字符。
while(fgets(message,80,stdin))
{
    l=strlen(message)-1;
    if(message[l]='\n') message[l]='\0';
            else message[i+1]='\0';
}

2
这不是一种保证的行为。请查看man fgets:"fgets()从流中读取最多比大小少一个字符,并将它们存储到指向s的缓冲区中。在EOF或换行符之后停止读取。如果读取到换行符,则将其存储到缓冲区中。在缓冲区中的最后一个字符之后存储终止空字节(aq\0aq)。"因此,当strlen(message)>80或使用EOF作为终止符时,可能会出现两种情况,换行符不会被添加。 - artdanil
1
如果(message[l] = '\n')则 message[l] = '\0' 是什么意思? - Jean-François Fabre

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