为什么是“;而(!feof(file))”;总是错吗?
使用为什么是“;而(!feof(file))”;总是错吗?,c,file,while-loop,eof,feof,C,File,While Loop,Eof,Feof,使用feof()控制读取循环有什么问题?例如: #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { char *path = "stdin"; FILE *fp = argc > 1 ? fopen(path=argv[1], "r") : stdin; if( fp == NULL ){
feof()
控制读取循环有什么问题?例如:
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char **argv)
{
char *path = "stdin";
FILE *fp = argc > 1 ? fopen(path=argv[1], "r") : stdin;
if( fp == NULL ){
perror(path);
return EXIT_FAILURE;
}
while( !feof(fp) ){ /* THIS IS WRONG */
/* Read and process data from file… */
}
if( fclose(fp) != 0 ){
perror(path);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#包括
#包括
int
主(内部argc,字符**argv)
{
char*path=“stdin”;
文件*fp=argc>1?fopen(路径=argv[1],“r”):stdin;
如果(fp==NULL){
佩罗尔(路径);
返回退出失败;
}
而(!feof(fp)){/*这是错误的*/
/*从文件中读取和处理数据*/
}
如果(fclose(fp)!=0){
佩罗尔(路径);
返回退出失败;
}
返回退出成功;
}
这个循环怎么了?不,它并不总是错的。如果您的循环条件是“当我们还没有尝试读取超过文件末尾时”,那么您可以使用
while(!feof(f))
。然而,这不是一个常见的循环条件-通常您想要测试其他东西(例如“我可以阅读更多内容吗”)虽然(!feof(f))
没有错,但它只是用错了。它错了,因为(在没有读取错误的情况下)它进入循环的次数比作者预期的要多一次。如果出现读取错误,循环永远不会终止
考虑以下代码:
/* WARNING: demonstration of bad coding technique!! */
#include <stdio.h>
#include <stdlib.h>
FILE *Fopen(const char *path, const char *mode);
int main(int argc, char **argv)
{
FILE *in;
unsigned count;
in = argc > 1 ? Fopen(argv[1], "r") : stdin;
count = 0;
/* WARNING: this is a bug */
while( !feof(in) ) { /* This is WRONG! */
fgetc(in);
count++;
}
printf("Number of characters read: %u\n", count);
return EXIT_SUCCESS;
}
FILE * Fopen(const char *path, const char *mode)
{
FILE *f = fopen(path, mode);
if( f == NULL ) {
perror(path);
exit(EXIT_FAILURE);
}
return f;
}
/*警告:演示错误的编码技术*/
#包括
#包括
文件*Fopen(常量字符*路径,常量字符*模式);
int main(int argc,字符**argv)
{
文件*in;
无符号计数;
in=argc>1?Fopen(argv[1],“r”):stdin;
计数=0;
/*警告:这是一个错误*/
而(!feof(in)){/*这是错误的*/
fgetc(in);
计数++;
}
printf(“读取的字符数:%u\n”,计数);
返回退出成功;
}
文件*Fopen(常量字符*路径,常量字符*模式)
{
文件*f=fopen(路径、模式);
如果(f==NULL){
佩罗尔(路径);
退出(退出失败);
}
返回f;
}
此程序将始终打印一个大于输入流中字符数的字符(假设没有读取错误)。考虑输入流为空的情况:
$ ./a.out < /dev/null
Number of characters read: 1
$./a.out
在本例中,feof()
在读取任何数据之前被调用,因此它返回false。进入循环后,调用fgetc()
(并返回EOF
),计数递增。然后调用feof()
,并返回true,导致循环中止
在所有这些情况下都会发生这种情况feof()
直到流上的读取遇到文件结尾后才返回true。feof()
的目的不是检查下一次读取是否会到达文件末尾。feof()
的目的是确定先前读取函数的状态
并区分错误条件和数据流的结尾。如果fread()
返回0,则必须使用feof
/ferror
来确定是否发生错误或是否消耗了所有数据。类似地,如果fgetc
返回EOF
feof()
仅在fread返回零或fgetc
返回EOF
后才有用。在此之前,feof()
将始终返回0
调用feof()
之前,始终需要检查读取的返回值(要么是fread()
,要么是fscanf()
,要么是fgetc()
)
更糟的是,考虑出现读取错误的情况。在这种情况下,
fgetc()
返回EOF
,feof()
返回false,循环永远不会终止。在所有使用(!feof(p))的情况下,必须至少在循环内部检查ferror()
,或者至少应将while条件替换为while(!feof(p)和&!ferror(p))
,或者存在无限循环的可能性,在处理无效数据时,可能会喷出各种垃圾
因此,总而言之,尽管我不能肯定地说,在任何情况下,当(!feof(f))时,写“在语义上都是正确的”(尽管必须在循环内部进行另一次带中断的检查,以避免读错误时出现无限循环),在这种情况下,几乎可以肯定它总是错的。即使出现了一个正确的情况,它的习惯用法也是错误的,因此它不是编写代码的正确方法。任何看到该代码的人都应该立即犹豫并说,“这是一个bug”。还可能打作者一巴掌(除非作者是你的老板,在这种情况下,建议谨慎处理。)feof()
表示是否试图读取文件末尾。这意味着它几乎没有预测效果:如果它是真的,你肯定下一个输入操作会失败(顺便说一句,你不确定上一个操作会失败),但是如果它是假的,你不确定下一个输入操作会成功。此外,输入操作可能会由于文件结尾以外的其他原因而失败(格式化输入的格式错误,所有输入类型的纯IO故障——磁盘故障、网络超时),因此即使您可以预测文件结尾(任何试图实现Ada one的人都会告诉你,如果你需要跳过空格,它可能会很复杂,并且会对交互设备产生不良影响——有时会在开始处理前一行之前强制输入下一行),你必须能够处理故障
因此,C中正确的习惯用法是以IO操作成功作为循环条件进行循环,然后测试失败的原因。例如:
while (fgets(line, sizeof(line), file)) {
/* note that fgets don't strip the terminating \n, checking its
presence allow to handle lines longer that sizeof(line), not showed here */
...
}
if (ferror(file)) {
/* IO failure */
} else if (feof(file)) {
/* format error (not possible with fgets, but would be with fscanf) or end of file */
} else {
/* format error (not possible with fgets, but would be with fscanf) */
}
我想提供一个抽象的、高层次的视角
并发性和同时性
I/O操作与环境交互。环境不是您程序的一部分,也不在您的控制之下。环境确实与您的程序“并发”存在。与所有并发的事物一样,questi
for (;;) {
size_t n = fread(buf, 1, bufsize, infile);
consume(buf, n);
if (n == 0) { break; }
}
for (int a, b, c; scanf("%d %d %d", &a, &b, &c) == 3; ) {
consume(a, b, c);
}
for (int n; std::cin >> n; ) {
consume(n);
}
for (std::string line; std::getline(std::cin, line); ) {
consume(line);
}
char const * p = buf;
ssize_t n = bufsize;
for (ssize_t k = bufsize; (k = write(fd, p, n)) > 0; p += k, n -= k) {}
if (n != 0) { /* error, failed to write complete buffer */ }
char *buffer = NULL;
size_t bufsiz = 0;
ssize_t nbytes;
while ((nbytes = getline(&buffer, &bufsiz, fp)) != -1)
{
/* Use nbytes of data in buffer */
}
free(buffer);
std::string input = " 123 "; // example
std::istringstream iss(input);
int value;
if (iss >> value >> std::ws && iss.get() == EOF) {
consume(value);
} else {
// error, "input" is not parsable as an integer
}
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *in = fopen("testfile.txt", "r");
while(1) {
char c = fgetc(in);
if (feof(in)) break;
printf("%c", c);
}
fclose(in);
return 0;
}
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *in = fopen("testfile.txt", "r");
while(!feof(in)) {
printf("%c", fgetc(in));
}
fclose(in);
return 0;
}