fgets() 在空字符串上不 return NULL
fgets() does not return NULL on empty string
出于代码安全原因,我最近尝试使用 fgets()
而不是 scanf()
来读取字符串。我使用了在这里找到的一个简单函数来检查错误(无输入和输入太长)。问题是每当我按下 "ENTER" 而实际上没有写任何东西时, fgets()
不会 return NULL
并且我的程序无法显示 NO_INPUT
错误.
这是main.cpp
:
#include <stdlib.h>
#include <stdio.h>
#include "utilities.h"
int main() {
int rc;
char str[20];
rc = getLine("Enter string: ", str, sizeof(str));
if(rc == NO_INPUT) {
printf("[ERROR] No input\n\n");
} else if(rc == TOO_LONG) {
printf("[ERROR] Input is too long\n\n");
} else {
printf("This is your input: \"%s\"\n\n", str);
}
system("pause");
}
这是utilities.h
:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
int getLine(const char *msg, char *buff, size_t len) {
if(msg != NULL) {
printf("%s", msg);
fflush(stdout);
}
if(fgets(buff, len, stdin) == NULL /*[+]*/ || strcmp(buff, "\n") == 0) {
//[-] fflush(stdin);
return NO_INPUT;
} else if(buff[strlen(buff)-1] != '\n') {
//[-] fflush(stdin);
return TOO_LONG;
} else {
buff[strlen(buff)-1] = '[=12=]';
//[-] fflush(stdin);
return OK;
}
}
这是输出:
输入字符串:
这是您的输入:“”
按任意键继续 。 . .
我通过将 utilities.h
中的第一个 if
语句替换为 if(fgets(buff, len, stdin) == NULL || strcmp(buff, "\n") == 0)
解决了我的问题。这样做,我的程序将检查输入错误或空字符串和 return NO_INPUT
标志。
感谢所有发表评论的人和@Peter 的回答。我在 utilities.h
中添加了上述 if
语句,并删除了每个 fflush(stdin)
事件。代码现在如上所示。
您 "fixed" 的问题是认为行尾应该被视为输入结束。
NULL
是 fgets()
的指示,它在从文件(或流)读取时遇到错误或输入结束。空行既不是错误也不是输入结束的标记。人类(在键盘上打字)可能会选择将换行符解释为输入结束,但计算机程序不会 - 毕竟,没有什么能阻止用户输入多行输入。
实际上,fgets()
读取一行并用 '\n'
字符指示该行的结尾。比方说,我们有一个包含
的文件
ABC
DE
FGHIJ
(空行穿插在三行文本中,后面是文件末尾)。
我们还假设 buffer
是一个包含五个 char
的数组,并且我们使用 fgets(buffer, 5, file)
.
形式的连续语句读取该文件
那么 fgets(buffer, 5, file)
每次调用会做什么。好吧,1 代表第一次调用,2 代表第二次调用,依此类推,我们将看到
的结果
"ABC\n"
存入buffer
;
"\n"
存入buffer
; (第一个空行)
"DE\n"
存入buffer
;
"\n"
存入buffer
; (第二个空行)
"FGHI"
存入buffer
;
"J\n"
存入buffer
;和
fgets()
returns NULL
,没有任何内容存储到 buffer
.
前六个调用将全部 return &buffer[0]
- 而不是 NULL
- 因为从文件读取时没有遇到错误。即使输入中有两个空行。比缓冲区长的最后一行(已计算 '\n'
)分两部分读取。
顺便说一下,您的代码正在使用 fflush(stdin)
。不幸的是,fflush()
只在 OUTPUT 流或文件上定义了行为。在 stdin
(或任何输入流)上使用它会产生未定义的行为。如果它实际上是在丢弃输入(它对 C 标准库的某些实现所做的),那么您很幸运——在现实世界的编译器中,结果行为不会丢弃输入。
出于代码安全原因,我最近尝试使用 fgets()
而不是 scanf()
来读取字符串。我使用了在这里找到的一个简单函数来检查错误(无输入和输入太长)。问题是每当我按下 "ENTER" 而实际上没有写任何东西时, fgets()
不会 return NULL
并且我的程序无法显示 NO_INPUT
错误.
这是main.cpp
:
#include <stdlib.h>
#include <stdio.h>
#include "utilities.h"
int main() {
int rc;
char str[20];
rc = getLine("Enter string: ", str, sizeof(str));
if(rc == NO_INPUT) {
printf("[ERROR] No input\n\n");
} else if(rc == TOO_LONG) {
printf("[ERROR] Input is too long\n\n");
} else {
printf("This is your input: \"%s\"\n\n", str);
}
system("pause");
}
这是utilities.h
:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
int getLine(const char *msg, char *buff, size_t len) {
if(msg != NULL) {
printf("%s", msg);
fflush(stdout);
}
if(fgets(buff, len, stdin) == NULL /*[+]*/ || strcmp(buff, "\n") == 0) {
//[-] fflush(stdin);
return NO_INPUT;
} else if(buff[strlen(buff)-1] != '\n') {
//[-] fflush(stdin);
return TOO_LONG;
} else {
buff[strlen(buff)-1] = '[=12=]';
//[-] fflush(stdin);
return OK;
}
}
这是输出:
输入字符串: 这是您的输入:“” 按任意键继续 。 . .
我通过将 utilities.h
中的第一个 if
语句替换为 if(fgets(buff, len, stdin) == NULL || strcmp(buff, "\n") == 0)
解决了我的问题。这样做,我的程序将检查输入错误或空字符串和 return NO_INPUT
标志。
感谢所有发表评论的人和@Peter 的回答。我在 utilities.h
中添加了上述 if
语句,并删除了每个 fflush(stdin)
事件。代码现在如上所示。
您 "fixed" 的问题是认为行尾应该被视为输入结束。
NULL
是 fgets()
的指示,它在从文件(或流)读取时遇到错误或输入结束。空行既不是错误也不是输入结束的标记。人类(在键盘上打字)可能会选择将换行符解释为输入结束,但计算机程序不会 - 毕竟,没有什么能阻止用户输入多行输入。
实际上,fgets()
读取一行并用 '\n'
字符指示该行的结尾。比方说,我们有一个包含
ABC
DE
FGHIJ
(空行穿插在三行文本中,后面是文件末尾)。
我们还假设 buffer
是一个包含五个 char
的数组,并且我们使用 fgets(buffer, 5, file)
.
那么 fgets(buffer, 5, file)
每次调用会做什么。好吧,1 代表第一次调用,2 代表第二次调用,依此类推,我们将看到
"ABC\n"
存入buffer
;"\n"
存入buffer
; (第一个空行)"DE\n"
存入buffer
;"\n"
存入buffer
; (第二个空行)"FGHI"
存入buffer
;"J\n"
存入buffer
;和fgets()
returnsNULL
,没有任何内容存储到buffer
.
前六个调用将全部 return &buffer[0]
- 而不是 NULL
- 因为从文件读取时没有遇到错误。即使输入中有两个空行。比缓冲区长的最后一行(已计算 '\n'
)分两部分读取。
顺便说一下,您的代码正在使用 fflush(stdin)
。不幸的是,fflush()
只在 OUTPUT 流或文件上定义了行为。在 stdin
(或任何输入流)上使用它会产生未定义的行为。如果它实际上是在丢弃输入(它对 C 标准库的某些实现所做的),那么您很幸运——在现实世界的编译器中,结果行为不会丢弃输入。