出于代码安全原因,我最近尝试使用fgets()
而不是scanf()
来读取字符串。我使用了一个简单的函数,我在这里检查错误(没有输入和太长的输入)。问题是每当我按“ENTER”而没有实际写任何内容时,fgets()
都不会返回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] = '\0';
//[-] fflush(stdin);
return OK;
}
}
这是输出:
Enter string: This is your input: "" Press any key to continue . . .
我通过用if
替换utilities.h
中的第一个if(fgets(buff, len, stdin) == NULL || strcmp(buff, "\n") == 0)
语句来解决我的问题。这样做,我的程序将检查输入错误或空字符串并返回NO_INPUT
标志。
感谢所有评论和@Peter回复的人。我在if
中添加了上述utilities.h
语句,并删除了每个fflush(stdin)
次出现。现在代码如上所示。
答案 0 :(得分:3)
您“修复”的问题是相信行尾会被视为输入结束。
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()
返回NULL
,并且buffer
中没有任何内容。前六个调用都将返回&buffer[0]
- 而不是NULL
- 因为从文件中读取时没有遇到错误。即使输入中有两个空行。最后一行比缓冲区长(计算'\n'
)分为两部分。
顺便提一下,您的代码正在使用fflush(stdin)
。不幸的是,fflush()
仅在OUTPUT流或文件上定义了行为。在stdin
(或任何输入流)上使用它会给出未定义的行为。如果它实际上丢弃了输入(它与C标准库的某些实现一样),那么你很幸运 - 有真实的编译器,其结果行为不会丢弃输入。