使用 read() 函数时检查 EOF

Posted

技术标签:

【中文标题】使用 read() 函数时检查 EOF【英文标题】:Checking for EOF when using read() function 【发布时间】:2014-07-24 18:56:37 【问题描述】:

这是我第一次从文件描述符中读取,我已经通过反复试验进行了大约 3 个小时的测试,我几乎让我的阅读器工作了!我只需要一点帮助来检查命名管道上的 EOF。

好的,我正在打开一个(多个)命名管道,如下所示:

fds[j].fd = open(pipeNameo, O_RDWR) ; // storing it into my file descriptor array

然后我正在轮询命名管道以查看是否有任何东西通过(轮询在循环内):

int ret = poll(fds, numOfPipesUsed, timeout_msecs);

当某些事情发生时,我通过将写入的文件描述符发送到此函数来处理文件:

int processFileDes( int fd )

    char buf[10] ;

    read(fd, buf, 1) ;
    char curr = buf[0] ;
    while (curr != EOF)
    

        if ( curr == ' ')
        
            // do nothing it is a space
        
        else if ( curr == '\n')
        
            printf("NEW LINE!\n") ;
        
        else
        
            int num = curr - '0' ; // turns char number into an int
            printf("Curr Num: %d\n", num) ;
        

        printf("BEFORE\n"); // Gets stuck here when EOF, this is the string of output
        read(fd, buf, 1) ;
        printf("AFTER\n") ;

        curr = buf[0] ;
    
printf("Success!\n") ; // this is never printed
return 0 ;

一切正常,除了 read() 函数在读取完所有字符后会卡住(我想是在等待返回)。应该是 EOF。我需要能够检查 EOF。

我尝试了一种解决方法,通过计算读取的字符数(因为我知道输入的大小)来阻止我的程序读取,并且它会起作用,唯一的问题是在最后一个字符之后有空格时导致我的循环轮询返回 1 并在剩余的待读取空间(无效输入)上再次运行 processFile()

请帮忙:')

输入只是一个数字矩阵,如下所示:

0 1 1
2 0 3
1 0 0

【问题讨论】:

【参考方案1】:

您应该首先确保您实际上是在管道上到达文件末尾。

当您打开一个 FIFO(命名管道的另一个名称)进行读取时,该进程将等待直到另一个进程打开它进行写入并写入一些内容。然后它将继续等待更多输入,直到 所有 将其打开以进行写入的进程都将其关闭。

您可以使用mkfifocat 在几个终端窗口中进行测试:

$ mkfifo test-fifo
$ cat test-fifo     # This is the reading process and will wait for data to read

在另一个终端:

$ cat > test-fifo   # This is the writing process; type here and see it above

现在如果你关闭写入进程,读取进程将退出,因为它已经到达文件末尾。

但是,如果你打开两个写进程,只退出一个,FIFO 会保持打开状态,读端不会结束,直到你都关闭它们。

因此,一种很容易犯错误的可能性是,在某处仍然打开 FIFO 以进行写入,因此读取端永远不会遇到文件结束的情况。需要注意的一件事是,如果该进程本身也打开了 FIFO 以供写入,或者生成数据的任何进程只是保持管道打开而不是在完成后关闭它。

如果您无法确定某个进程是否打开了管道以供写入,请尝试fuserlsof 来识别任何可能打开它的进程。

您的代码中的其他一些 cmets,也可能是问题:

char buf[10] ;

read(fd, buf, 1) ;
char curr = buf[0] ;
while (curr != EOF)

这不是您使用低级、无缓冲read 原语确定是否到达文件末尾的方式;这就是您使用像fgetc 这样的缓冲stdio 操作的方法。使用read,您可以使用所需的读取长度调用它,它会返回它可以读取的长度(可能小于您要求的长度),如果它在文件末尾,则返回 0,然后返回 -1关于错误。如果您没有检查read 的返回值,那么您有一个错误。请注意,并非所有错误都是致命的。

即使您使用的是fgetc,这段代码也有问题。 fgetc 返回一个 int,而不是一个 char,因此它可以返回完整的字符值范围以及 EOF 的标记值。

    printf("BEFORE\n"); // Gets stuck here when EOF, this is the string of output
    read(fd, buf, 1) ;
    printf("AFTER\n") ;

您通常不应该在两次轮询调用之间尝试读取相同的文件描述符两次;否则,如果文件描述符上没有更多可用数据,则第二个可能会阻塞。相反,您应该读取一定数量的数据,但要准备好read 返回的数据少于您请求的数据,然后返回轮询循环以找出现在可用的数据(可能在不同的 fd 上) ,如果这个没有可用的新数据,但另一个有)。

read的使用方式一般是一次读取整个缓冲区,而不是一次读取一个字符。

【讨论】:

我以前从未听说过 fuserlsof。此外,恕我直言,这是一个完美的总结和管道/fifo陷阱的提示。 +1 谢谢。【参考方案2】:

您需要打开非阻塞,因为管道将等待数据可用:

fds[j].fd = open(pipeNameo, O_RDWR | O_NONBLOCK);

【讨论】:

【参考方案3】:

读取的返回值 0 表示 EOF。

【讨论】:

问题是,一旦达到 EOF,它就永远不会返回。 read() 卡在循环的最后一次迭代中。 循环结束时的 printf("AFTER\n") 永远不会打印最后的时间,这意味着 read() 在尝试读取并且已经是 EOF 时永远不会完成。 @DavidBaez 您的代码从不检查读取的返回值。此外,由于您正在从管道读取,因此在管道的另一端关闭之前您不会看到 EOF。有这种情况吗? @DavidBaez 如果 read() 返回 0,则不应再次调用 read()。您的代码需要检查 read() 的返回值并处理返回值 0(EOF)和 -1(错误)。您还需要确保命名管道的另一端实际上是关闭的。在另一端关闭之前不会有 EOF。

以上是关于使用 read() 函数时检查 EOF的主要内容,如果未能解决你的问题,请参考以下文章

检查 string::getline 中的 eof

php文件操作函数feof函数使用方法

“EOF”字符的十六进制代码在哪里?

read.csv 警告“引用字符串中的 EOF”阻止完整读取文件

在 __repr__() 函数中使用 f 字符串时出现 EOF 错误

chars eof啥用