为什么“,而(!的feof(文件))”永远是错的? [英] Why is “while ( !feof (file) )” always wrong?

查看:179
本文介绍了为什么“,而(!的feof(文件))”永远是错的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我见过人们尝试在很多帖子最近读过这样的文件。

I've seen people trying to read files like this in a lot of posts lately.

#include <stdio.h>
#include <stdlib.h>

int main( int argc, char **argv )
{
    char * path = argc > 1 ? argv[1] : "input.txt";
    FILE * fp = fopen( path, "r" );
    if( fp == NULL ) {
        perror( path );
        return EXIT_FAILURE;
    }
    while( !feof( fp )) {  /* THIS IS WRONG */
        /* Read and process data from file… */
    }
    fclose( fp );
    return EXIT_SUCCESS;
}

什么是错的这个而(!的feof(FP))循环?

推荐答案

我想提供一个抽象的,高层次的角度来看。

I'd like to provide an abstract, high-level perspective.

I / O操作与环境互动。环境是不是你的计划的一部分,而不是你的控制之下。环境真正符合你的程序同时的存在。由于并发所有的东西,关于当前状态的问题没有意义:没有同时跨越并发事件的概念。许多国家的性质根本就没有的存在的并发。

I/O operations interact with the environment. The environment is not part of your program, and not under your control. The environment truly exists "concurrently" with your program. As with all things concurrent, questions about the "current state" don't make sense: There is no concept of "simultaneity" across concurrent events. Many properties of state simply don't exist concurrently.

让我做这更precise:假设你要问,你有更多的数据。你可以问这样一个并发的容器,或者你的I / O系统。但答案是通常不可诉,因此毫无意义。那么,如果容器说是&ndash的;你的时间尝试阅读,它可能不再有数据。同样地,如果答案是否,通过在尝试读出时间,数据可能已经到达。结论是,目前根本的像我有数据,因为你不能回应任何可能的答案有意义的行动没有财产。 (情况稍好是缓冲输入,在那里你可以设想得到一个是的,我的数据,构成了某种保证,但你仍然必须能够处理相反的情况下​​,并且随着产量的情况是因为我的确描述一样糟糕:你永远不知道如果硬盘或网络缓冲区已满)

Let me make this more precise: Suppose you want to ask, "do you have more data". You could ask this of a concurrent container, or of your I/O system. But the answer is generally unactionable, and thus meaningless. So what if the container says "yes" – by the time you try reading, it may no longer have data. Similarly, if the answer is "no", by the time you try reading, data may have arrived. The conclusion is that there simply is no property like "I have data", since you cannot act meaningfully in response to any possible answer. (The situation is slightly better with buffered input, where you might conceivably get a "yes, I have data" that constitutes some kind of guarantee, but you would still have to be able to deal with the opposite case. And with output the situation is certainly just as bad as I described: you never know if that disk or that network buffer is full.)

因此​​,我们得出结论,这是不可能的,事实上联合国的合理的,要问的I / O系统是否的将会的能够执行I / O操作。我们可以与它进行交互(就像一个容器并发),唯一可能的方式是的尝试的操作,并检查它是否成功还是失败。在那一刻,你与环境互动,那么只有到那时,你就可以知道是否交互实际上是可能的,在这一点上,你必须承诺进行互动。 (这是一个同步点,如果你会的。)

So we conclude that it is impossible, and in fact unreasonable, to ask an I/O system whether it will be able to perform an I/O operation. The only possible way we can interact with it (just as with a concurrent container) is to attempt the operation and check whether it succeeded or failed. At that moment where you interact with the environment, then and only then can you know whether the interaction was actually possible, and at that point you must commit to performing the interaction. (This is a "synchronisation point", if you will.)

现在,我们得到EOF。 EOF是的响应的您从试图获取的I / O操作。这意味着你试图读或写东西,但这样做你无法读取或写入任何数据时,反而遇到了输入或输出的结束。本作基本上所有的I / O API的,无论是C标准库,C ++输入输出流,或者其他库是真实的。只要在I / O操作成功,您只需无法知道的是否需要进一步,未来的操作会成功。您的必须的总是先试操作,然后成功或失败的响应。

Now we get to EOF. EOF is the response you get from an attempted I/O operation. It means that you were trying to read or write something, but when doing so you failed to read or write any data, and instead the end of the input or output was encountered. This is true for essentially all the I/O APIs, whether it be the C standard library, C++ iostreams, or other libraries. As long as the I/O operations succeed, you simply cannot know whether further, future operations will succeed. You must always first try the operation and then respond to success or failure.

在每个例子中,仔细注意,我们的第一个的尝试I / O操作的然后的消耗的结果,如果它是有效的。进一步注意到我们总是的,必须使用的I / O操作的结果,虽然其结果取不同的形状和形式在各实施例

In each of the examples, note carefully that we first attempt the I/O operation and then consume the result if it is valid. Note further that we always must use the result of the I/O operation, though the result takes different shapes and forms in each example.


  • C STDIO,从文件中读取:

  • C stdio, read from a file:

for (;;) {
    size_t n = fread(buf, 1, bufsize, infile);
    consume(buf, n);
    if (n < bufsize) { break; }
}

我们必须使用其结果是 N 元素中读取的次数(可能是低至零)。

The result we must use is n, the number of elements that were read (which may be as little as zero).

C STDIO, scanf函数

for (int a, b, c; scanf("%d %d %d", &a, &b, &c) == 3; ) {
    consume(a, b, c);
}

我们必须使用其结果是 scanf函数的返回值,换算元素的数量。

The result we must use is the return value of scanf, the number of elements converted.

C ++格式的iostream提取:

C++, iostreams formatted extraction:

for (int n; std::cin >> n; ) {
    consume(n);
}

我们必须使用其结果是的std :: CIN 本身,它可以在布尔环境进行评估,并告诉我们流是否仍然在好()状态。

The result we must use is std::cin itself, which can be evaluated in a boolean context and tells us whether the stream is still in the good() state.

C ++的iostream函数getline:

C++, iostreams getline:

for (std::string line; std::getline(std::cin, line); ) {
    consume(line);
}

我们必须使用其结果又是的std :: CIN ,就像以前一样。

The result we must use is again std::cin, just as before.

POSIX,写(2)刷新缓冲区:

POSIX, write(2) to flush a buffer:

char const * p = buf;
ssize_t n = bufsize;
for (ssize_t k = bufsize; k = write(fd, p, n); p += k, n -= k) {}
if (n != 0) { /* error, failed to write complete buffer */ }

我们这里使用的结果是 K ,写入的字节数。这里的关键是,我们只能知道有多少字节写的之后的写操作。

The result we use here is k, the number of bytes written. The point here is that we can only know how many bytes were written after the write operation.

POSIX <一个href=\"http://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html\"><$c$c>getline()

POSIX getline()

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);

我们必须使用其结果是的nbytes ,字节数直至并包括换行符(或EOF如果文件没有以换行符结尾)。

The result we must use is nbytes, the number of bytes up to and including the newline (or EOF if the file did not end with a newline).

请注意该函数返回明确 1 (而不是EOF!)发生错误时,或达到EOF。

Note that the function explicitly returns -1 (and not EOF!) when an error occurs or it reaches EOF.

您可能会注意到,我们很少拼出实际单词EOF。我们通常检测更有趣立即给我们的是一些其他的方式出现的错误(例如,未能履行尽可能多的I / O正如我们所期望的)。在每一个例子有一些API的功能,可以告诉我们明确了国家EOF已经遇到,但这其实不是的信息非常有用的棋子。这是更细节的比我们通常所关心的。要紧的是I / O是否成功,比它是如何失败更左右。

You may notice that we very rarely spell out the actual word "EOF". We usually detect the error condition in some other way that is more immediately interesting to us (e.g. failure to perform as much I/O as we had desired). In every example there is some API feature that could tell us explicitly that the EOF state has been encountered, but this is in fact not a terribly useful piece of information. It is much more of a detail than we often care about. What matters is whether the I/O succeeded, more-so than how it failed.


  • 这实际查询EOF状态最后一个例子:假设你有一个字符串,并希望测试其重新presents整体的整数,除空格结尾没有多余的比特。使用C ++输入输出流,它是这样的:

  • A final example that actually queries the EOF state: Suppose you have a string and want to test that it represents an integer in its entirety, with no extra bits at the end except whitespace. Using C++ iostreams, it goes like this:

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
}

我们在这里使用两个结果。首先是 ISS ,流对象本身,以检查格式化萃取成功。但随后,又经过耗费空白,我们执行另一个I / O /操作, iss.get(),并期望它失败,因为EOF,这是,如果情况整个字符串已被格式化的提取食用。

We use two results here. The first is iss, the stream object itself, to check that the formatted extraction to value succeeded. But then, after also consuming whitespace, we perform another I/O/ operation, iss.get(), and expect it to fail as EOF, which is the case if the entire string has already been consumed by the formatted extraction.

在C标准库可以实现通过检查结束指针已经到达了输入字符串的结束与 strto * L 功能类似的东西。

In the C standard library you can achieve something similar with the strto*l functions by checking that the end pointer has reached the end of the input string.

而(!EOF)是错误的,因为它测试的东西是不相关的,没有测试,你需要知道的东西。其结果是,你被错误执行code,它假定它访问被成功读取数据的时候,其实这从来没有发生过。

while(!eof) is wrong because it tests for something that is irrelevant and fails to test for something that you need to know. The result is that you are erroneously executing code that assumes that it is accessing data that was read successfully, when in fact this never happened.

这篇关于为什么“,而(!的feof(文件))”永远是错的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆