PHP Conference Japan 2024

feof

(PHP 4, PHP 5, PHP 7, PHP 8)

feof测试文件指针是否到了文件结束的位置

说明

feof(resource $stream): bool

测试文件指针是否到了文件结束的位置。

参数

stream

文件指针必须有效,并且必须指向一个由 fopen()fsockopen() 成功打开的文件(且还未被 fclose() 关闭)。

返回值

如果文件指针到了 EOF 或者出错时(包括 socket 超时),则返回 true,否则返回 false

注释

警告

如果一个由 fsockopen() 打开的连接未被服务器关闭,feof() 将会挂起。要解决此问题,请参见下面的示例

示例 #1 使用 feof() 处理超时

<?php
function safe_feof($fp, &$start = NULL) {
$start = microtime(true);

return
feof($fp);
}

/* 假设 $fp 已由 fsockopen() 打开 */

$start = NULL;
$timeout = ini_get('default_socket_timeout');

while(!
safe_feof($fp, $start) && (microtime(true) - $start) < $timeout)
{
/* 处理 */
}
?>

警告

如果传递的文件指针无效,可能会导致无限循环,因为 feof() 无法返回 true

示例 #2 使用无效文件指针的 feof() 示例

<?php
// 如果文件无法读取或不存在,fopen 函数将返回 FALSE
$file = @fopen("no_such_file", "r");

// 来自 fopen 的 FALSE 将发出警告并导致此处的无限循环
while (!feof($file)) {
}

fclose($file);
?>

添加注释

用户贡献的注释 16 条

up
50
ironoxid at libero dot it
18 年前
我真的以为当逻辑文件指针位于 EOF 时,feof() 会返回 TRUE。
但事实并非如此!
我们需要读取并获得一个空记录,然后 eof() 才会报告 TRUE。

所以

$fp = fopen('test.bin','rb');
while(!feof($fp)) {
$c = fgetc($fp);
// ... 对 $c 进行一些操作
echo ftell($fp), ",";
}
echo 'EOF!';

会两次打印最后一个字节的位置。
如果我们的文件长度为 5 个字节,此代码将打印

0,1,2,3,4,5,5,EOF!

因此,你必须进行另一次检查以验证 fgetc 是否真的读取了另一个字节(以防止在“对 $c 进行一些操作”时出错 ^_^)。

为了防止错误,你必须使用此代码

$fp = fopen('test.bin','rb');
while(!feof($fp)) {
$c = fgetc($fp);
if($c === false) break;
// ... 对 $c 进行一些操作
}

但这与

$fp = fopen('test.bin','rb');
while(($c = fgetc($fp))!==false) {
// ... 对 $c 进行一些操作
}

的结果相同。因此 feof() 根本没用。
在写这条注释之前,我想将其作为 php 错误提交,但一位 php 开发人员说这并不意味着 PHP 本身存在错误 (http://bugs.php.net/bug.php?id=35136&edit=2)。

如果这不是错误,我认为至少需要注意这一点。

抱歉,我的英语不好。
再见 ;)
up
15
sudo dot adam dot carruthers at gmail dot com
14 年前
在 TCP 流上使用 feof() 时,我发现以下方法有效(经过数小时的沮丧和愤怒之后)

注意:我使用“;”表示数据传输结束。可以将其修改为服务器的文件结束符或在本例中为输出结束字符。

<?php
$cursor
= "";
$inData = "";

while(
strcmp($cursor, ";") != 0) {
$cursor = fgetc($sock);
$inData.= $cursor;
}
fclose($sock);
echo(
$inData);
?>

由于 strcmp() 在两个字符串相等时返回 0,因此只要光标不是“;”,它就会返回非零值。使用上述方法会将“;”添加到字符串中,但解决方法很简单。

<?php
$cursor
= "";
$inData = "";

$cursor = fgetc($sock);
while(
strcmp($cursor, ";") != 0) {
$inData.= $cursor;
}
fclose($sock);
echo(
$inData);
?>

我希望这对某人有所帮助。
up
4
DimeCadmium
6 年前
feof() 并不是测试文件的实际结尾,而是测试一种称为文件结尾的异常情况。(它基于同名的 C 函数,该函数“测试流的文件结尾指示符”)

也就是说,feof() 仅告诉您 fread()(及其相关函数)是否遇到了 EOF,以允许您将其与其他错误区分开来。您应该测试 fread()(或您用来读取的任何函数)的返回值,而不是 feof()。

特别是,如果您的文件句柄无效(文件不存在/权限问题/等)或 fread() 遇到其他错误,feof 将返回 false,并且您的代码将运行一个无限循环来处理 fread() 返回的 FALSE。

来自 fread() 的(C)手册页
fread() 不区分文件结尾和错误,调用者
必须使用 feof(3) 和 ferror(3) 来确定发生了哪一个。
这是 feof() 的唯一目的。
up
9
Tom
18 年前
实际上,feof() 是可靠的。但是,您必须将其与 fgets() 结合使用并小心处理。一种常见(但不正确)的用法如下:

<?
$fp = fopen("myfile.txt", "r");
while (!feof($fp)) {
$current_line = fgets($fp);
// 在此处处理当前行
}
fclose($fp);
?>

处理纯文本文件时的问题在于,在获取最后一行输入后,feof() 不会返回 true。您需要尝试获取输入_并失败_,feof() 才会返回 true。您可以将上面的循环理解为这样工作:

* (愉快地循环,获取行并处理它们)
* fgets 用于获取倒数第二行
* 处理行
* 循环回去 -- feof 返回 false,因此执行循环内的步骤
* fgets 用于获取最后一行
* 处理行
* 循环回去 -- 由于上次调用 fgets 有效(您获取了最后一行),feof 仍然返回 false,因此您再次执行循环内的步骤
* fgets 尝试获取另一行(但那里什么也没有!)
* 您的代码没有意识到这一点,并尝试处理这个不存在的行(通常是通过再次执行相同的操作)
* 现在当您的代码循环回去时,feof 返回 true,并且您的循环结束

有两种方法可以解决这个问题:

1. 您可以在循环内添加一个额外的 feof() 测试
2. 您可以移动对 fgets() 的调用,以便 feof() 的测试发生在更好的位置

这是解决方案 1:

<?
$fp = fopen("myfile.txt", "r");
while(!feof($fp)) {
$current_line = fgets($fp);
if (!feof($fp)) {
// 处理当前行
}
}
fclose($fp);
?>

这是解决方案 2(恕我直言,更优雅)

<?
$fp = fopen("myfile.txt", "r");
$current_line = fgets($fp);
while (!feof($fp)) {
// 处理当前行
$current_line = fgets($fp);
}
fclose($fp);
?>

顺便说一句,C++ 中的 eof() 函数的工作方式完全相同,所以这不仅仅是一些奇怪的 PHP 特性...
up
3
line dot loic at gmail dot com
9 年前
为了避免无限循环和警告:
"警告:feof() 期望参数 1 为资源,但给定的是布尔值"

您需要检查 fopen 函数是否返回正确的类型。
这可以使用 gettype() 轻松实现。
这是一个例子:

$source = fopen($xml_uri, "r");
$xml = "";

if(gettype($source) == "resource") { // 在这里检查!
while (!feof($source)) {
$xml .= fgets($source, 4096);
}
}

echo $xml;
up
3
honzam+php at ipdgroup dot com
16 年前
Johannes:请记住 stream_get_meta_data 页面的说明:对于套接字流,即使 unread_bytes 不为零,此成员 [eof] 也可能为 TRUE。要确定是否有更多数据要读取,请使用 feof() 而不是读取此项。

另一件事:最好不要依赖 feof 何时返回 true 的“包括套接字超时”部分。刚刚发现程序在 while(!feof($fd)) fread ... 中循环了两天,在 PHP 4.3.10 中超时时间为 20 秒。
up
2
匿名
18 年前
如果您使用 fseek 函数将指针位置设置到超出文件大小,feof 仍然返回 true。因此请注意,当您使用 feof 作为 while 循环的条件时。
up
3
m a p o p a at g m a i l. c o m
18 年前
您可以避免无限循环和填充错误日志
通过一个简单的 if 语句
这是一个例子

$handle = fopen("http://xml.weather.yahoo.com/forecastrss?p=AYXX0008&u=f", "r");
$xml = "";
if ($handle)
{
while (!feof($handle))
{
$xml .= fread($handle, 128);
}
fclose($handle);
}
up
4
cmr at forestfactory dot de
18 年前
这是解决方案 3

<?
$fp = fopen("myfile.txt", "r");
while ( ($current_line = fgets($fp)) !== false ) {
// 在此处处理当前行
}
fclose($fp);
?>

据我所知,fgets() 从不返回空字符串,所以我们也可以写成

<?
$fp = fopen("myfile.txt", "r");
while ( $current_line = fgets($fp) ) {
// 在此处处理当前行
}
fclose($fp);
?>
up
2
匿名
19 年前
如果您担心文件指针无效,请在进入循环之前对其进行测试...这样它就永远不会成为无限循环。
up
2
dewi at dewimorgan dot com
12 年前
文档中的返回值说明不正确。它说:

如果文件指针位于 EOF 或发生错误(包括套接字超时),则返回 TRUE;否则返回 FALSE。

正确的文本更像是:

如果未传递文件句柄,则返回 FALSE;
如果未传递文件句柄,则返回 NULL;
如果文件指针位于 EOF 或发生错误(包括套接字超时),则返回 TRUE;
否则返回 FALSE。

例如,从命令行运行以下命令:

php -r 'echo
"空:".var_export(feof(), true)."\n".
"空:".var_export(feof(NULL), true)."\n".
"未定义:".var_export(feof($undef), true)."\n"
;'

这将输出:

PHP 警告:feof() 的参数计数错误,在命令行代码的第 1 行
PHP 警告:feof():提供的参数不是有效的流资源,在命令行代码的第 1 行
PHP 警告:feof():提供的参数不是有效的流资源,在命令行代码的第 1 行

空:NULL
空:false
未定义:false

正如其他评论者所报告的那样,如果 fopen() 返回的文件句柄由于任何原因无效,这可能导致无限循环和大量的 PHP 错误日志文件。
up
2
Jet
17 年前
要避免 fgets() 的无限循环,只需使用 do..while 语句。

<?php
if ($f = fopen('myfile.txt', 'r')) do {
$line = fgets($f);
// 在这里做任何事情...
} while (!feof($f));
fclose($f);
up
1
Alwar
11 年前
不要使用 feof 来测试您是否已读取套接字另一端发送的所有数据。据我所知,只有当另一端关闭连接时,它才会返回 true。
up
0
lulzury at Yahoo dot com
9 年前
来自 washington dot edu css342
在 unix/linux 上,文件中的每一行都有一个行尾 (EOL) 字符,EOF 字符在最后一行之后。在 windows 上,除了最后一行外,每一行都有一个 EOL 字符。所以 unix/linux 文件的最后一行是:
内容, EOL, EOF
而 windows 文件的最后一行,如果光标在该行上,则是:
内容, EOF

因此,将 windows 上的数据文件设置为与 unix/linux 上相同。这样,您就可以在 unix/linux 和 windows 下正确确定 eof。通常,当您尝试读取超过 eof 的项目时,您必须立即退出所有循环和所有函数。

这是一个可以正常工作的典型设置。假设在数据文件中,有多行数据。在某个函数中是您正在读取和处理此数据的循环。此循环将类似于以下内容。
// 读取和处理一行数据的无限循环
for (;;) {
$ln = fgets($fp);
if (feof($fp)) break;

// 读取该行的其余部分
// 对数据做任何事情
}
如果您不喜欢无限循环,则可以通过启动循环并在最后再次读取来使用 while 循环完成相同的操作
$ln = fgets($fp);
while (!feof($fp)) {
// 读取该行的其余部分
// 对数据做任何事情

$ln = fgets($fp);
}
up
0
jakicoll
14 年前
请注意,与 TCP 连接一起使用的 feof() 会在连接打开时返回 false。
即使没有可用数据,它也会返回 false。

顺便说一句:将 feof() 与 HTTP 一起用于单个请求时,您应该始终确保将 HTTP 标头“Connection”设置为“close”而_不是_设置为“keep-alive”。
up
0
Johannes
20 年前
我发现 feof() 在使用非阻塞连接时是一个缓慢的函数。

函数 stream_get_meta_data() 返回速度更快,并且有一个返回字段“eof”。
To Top