PHP Conference Japan 2024

feof

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

feof測試檔案指標是否已到達檔案結尾

說明

feof(資源 $stream): 布林值

測試檔案指標是否已到達檔案結尾。

參數

stream

檔案指標必須有效,並且必須指向一個已透過 fopen()fsockopen() 成功開啟且尚未透過 fclose() 關閉的檔案。

返回值

如果檔案指標已到達檔案結尾或發生錯誤(包括通訊端逾時),則返回 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 則註解

ironoxid at libero dot it
18 年前
我原以為當邏輯檔案指標位於檔案結尾 (EOF) 時,feof() 會回傳 TRUE。
但並非如此!
我們需要讀取並取得一個空的紀錄,feof() 才會回傳 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');
當(($c = fgetc($fp))!==false) {
// ... 處理 $c
}

因此,feof() 根本沒用。
在寫這篇筆記之前,我想要將這個問題提交為 PHP 的 bug,但一位 PHP 開發人員表示這並不意味著 PHP 本身有 bug (http://bugs.php.net/bug.php?id=35136&edit=2)。

如果這不是 bug,我認為至少需要注意這一點。

抱歉,我的英文不好。
再見 ;)
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;
$cursor = fgetc($sock);
}
fclose($sock);
echo(
$inData);
?>

我希望這能幫助到某些人。
DimeCadmium
6 年前
feof() 並非測試檔案的實際結尾,而是測試一種稱為檔案結尾的例外狀況。(它基於同名的 C 函式,該函式「測試串流的檔案結尾指示器」)

也就是說,feof() 只會告訴您 fread()(以及相關函式)是否遇到 EOF,以便您將其與其他錯誤區分開來。您應該測試 fread()(或您用於讀取的任何函式)的返回值,而不是 feof()。

特別是,如果您的檔案控制代碼無效(檔案不存在/權限問題/等等)或者 fread() 遇到其他錯誤,feof 將返回 false,您的程式碼將會執行無限迴圈來處理從 fread() 返回的 FALSE。

摘自 fread() 的 (C) 使用手冊頁面
fread() 無法區分檔案結尾和錯誤,呼叫者
必須使用 feof(3) 和 ferror(3) 來判斷發生了哪一種情況。
這就是 feof() 的唯一用途。
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 的怪異之處...
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;
honzam+php at ipdgroup dot com
16 年前
Johannes:請記住 stream_get_meta_data 頁面中的注意事項:對於通訊端串流,即使 unread_bytes 不為零,此成員 [eof] 也可能為 TRUE。要判斷是否有更多資料要讀取,請使用 feof() 而不是讀取此項目。

另一件事:最好不要依賴 feof 返回 true 時的「包含通訊端逾時」部分。剛發現程式在 PHP 4.3.10 中以 20 秒逾時在 while(!feof($fd)) fread ... 中循環了兩天。
匿名
18 年前
如果您使用 fseek 函數將指標定位到超過檔案大小的位置,feof 仍然會返回 true。因此,當您使用 feof 作為 while 迴圈的條件時,請注意這一點。
m a p o p a at g m a i l. c o m
18 年前
您可以透過一個簡單的 if 敘述來避免無限迴圈和填滿錯誤日誌。
透過一個簡單的 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);
}
cmr at forestfactory dot de
18 年前
以下是解決方案 3

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

AFAICS fgets() 永遠不會返回空字串,所以我們也可以寫成

<?
$fp = fopen("myfile.txt", "r");
while ( $current_line = fgets($fp) ) {
// 在此處處理目前行
}
fclose($fp);
?>
匿名
19 年前
如果您擔心檔案指標無效,請在進入迴圈之前測試它...這樣它就永遠不會變成無限迴圈。
dewi at dewimorgan dot com
12 年前
文件中關於返回值的說明有誤。它說

如果檔案指標位於 EOF 或發生錯誤(包括通訊端逾時),則返回 TRUE;否則返回 FALSE。

正確的文字應該更像是

如果未傳遞檔案控制代碼,則返回 FALSE;
如果未傳遞檔案控制代碼,則返回 NULL;
如果檔案指標位於 EOF 或發生錯誤(包括通訊端逾時),則返回 TRUE;
否則返回 FALSE。

例如,從命令列執行以下程式碼

php -r 'echo
"Empty: ".var_export(feof(), true)."\n".
"Null: ".var_export(feof(NULL), true)."\n".
"Undefined: ".var_export(feof($undef), true)."\n"
;'

這將輸出

PHP 警告:feof() 的參數數量錯誤,位於第 1 行的命令列程式碼中
PHP 警告:feof():提供的引數不是有效的串流資源,位於第 1 行的命令列程式碼中
PHP 警告:feof():提供的引數不是有效的串流資源,位於第 1 行的命令列程式碼中

Empty: NULL
Null: false
Undefined: false

正如其他評論者所報告的,如果 fopen() 返回的檔案控制代碼由於任何原因無效,這可能會導致無限迴圈和大量的 PHP 錯誤日誌檔案。
Jet
17 年前
要避免 fgets() 的無限迴圈,只需使用 do..while 敘述。

<?php
if ($f = fopen('myfile.txt', 'r')) do {
$line = fgets($f);
// 在這裡做任何事情...
} while (!feof($f));
fclose($f);
Alwar
11 年前
不要使用 feof 來測試是否已讀取到 socket 另一端傳送的所有資料。據我所知,它只會在對方關閉連線時才會返回 true。
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;

// 讀取該行的其餘部分
// 處理資料
}
}
$ln = fgets($fp);
while (!feof($fp)) {
// 讀取該行的其餘部分
// 處理資料

$ln = fgets($fp);
}
jakicoll
14 年前
請注意,feof() 與 TCP 連線一起使用時,只要連線開啟,就會返回 false。
即使沒有可用資料,它也會返回 false。

順帶一提:在單一請求中將 feof() 與 HTTP 一起使用時,您應始終確保將 HTTP 標頭「Connection」設定為「close」,而不是「keep-alive」。
Johannes
20 年前
我發現 feof() 在使用非阻塞連線時速度很慢。

函式 stream_get_meta_data() 返回速度更快,並且有一個返回欄位 'eof'。
To Top