PHP Conference Japan 2024

fgets

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

fgets從檔案指標取得一行

說明

fgets(資源 $stream, ?int $length = null): 字串|false

從檔案指標取得一行。

參數

stream

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

length

讀取會在讀取 length - 1 個位元組後結束,或者遇到換行符號(包含在返回值中)或檔案結尾(EOF)(以先發生的為準)。如果未指定長度,它將持續從串流讀取,直到到達行尾。

回傳值

返回從 stream 指向的檔案中讀取的最多 length - 1 個位元組的字串。如果檔案指標中沒有更多資料可讀取,則返回 false

如果發生錯誤,則返回 false

範例

範例 #1 逐行讀取檔案

<?php

$fp
= @fopen("/tmp/inputfile.txt", "r");

if (
$fp) {
while ((
$buffer = fgets($fp, 4096)) !== false) {
echo
$buffer, PHP_EOL;
}

if (!
feof($fp)) {
echo
"Error: unexpected fgets() fail\n";
}

fclose($fp);
}

?>

注意事項

注意如果 PHP 在讀取由 Macintosh 電腦建立或在 Macintosh 電腦上讀取檔案時無法正確辨識行尾,啟用 auto_detect_line_endings 執行階段設定選項可能有助於解決此問題。

備註:

習慣使用 'C' 語言語法的 fgets() 的使用者應注意其返回 EOF 方式的差異。

參見

新增註解

使用者貢獻的註解 5 則註解

Leigh Purdie
9 年前
一個更好的例子,用來說明 fgets 和 stream_get_line 對於大型檔案在速度上的差異。

此範例模擬從輸入來源讀取長度不確定(但有最大緩衝區大小)的潛在超長行的狀況。

正如 Dade 指出的,我之前提供的例子很容易被拆解,並且沒有充分突顯我試圖解決的問題。

請注意,為 fgets 指定明確的結束字元(例如:換行符)通常會合理地顯著減小速度差異。

#!/usr/bin/php
<?php
$plaintext
=file_get_contents('http://loripsum.net/api/60/verylong/plaintext'); # 應該大約有 90k 個字元
$plaintext=str_replace("\n"," ",$plaintext); # 去除換行符號

$fp=fopen("/tmp/SourceFile.txt","w");
for(
$i=0;$i<100000;$i++) {
fputs($fp,substr($plaintext,0,rand(4096,65534)) . "\n");
}
fclose($fp);

$fp=fopen("/tmp/SourceFile.txt","r");
$start=microtime(true);
while(
$line=fgets($fp,65535)) {
1;
}
$end=microtime(true);
fclose($fp);
$delta1=($end - $start);

$fp=fopen("/tmp/SourceFile.txt","r");
$start=microtime(true);
while(
$line=stream_get_line($fp,65535)) {
1;
}
$end=microtime(true);
fclose($fp);
$delta2=($end - $start);

$pdiff=$delta1/$delta2;
print
"stream_get_line 比 fgets " . ($pdiff>1?"更快":"更慢") . " - 效能差異比為 $pdiff\n";
?>

$ ./testcase.php
stream_get_line 比 fgets 更快 - 效能差異比為 1.760398041785

請注意,在絕大多數使用 PHP 的情況下,系統呼叫之間微小的速度差異是可以忽略的。
匿名
4 年前
如果您因為某些原因需要從字串而不是檔案指標取得一行一行資料,請嘗試:

<?php
function string_gets(string $source, int $offset = 0, string $delimiter = "\n"): ?string
{
$len = strlen($source);
if (
$len < $offset) {
// 超出界限.. 也許我應該拋出一個例外
return null;
}
if (
$len === $offset) {
// 字串結尾..
return null;
}
$delimiter_pos = strpos($source, $delimiter, $offset);
if (
$delimiter_pos === false) {
// 最後一行.
return substr($source, $offset);
}
return
substr($source, $offset, ($delimiter_pos - $offset) + strlen($delimiter));
}

?>

(我有一個大約 16GB 的字串在記憶體中需要逐行處理,但如果我嘗試使用 explode("\n",$str);,則會發生記憶體配置崩潰(在 32GB RAM 系統上),所以想出了這個方法.. 有趣的是,fgets() 似乎比在 PHP 的記憶體中執行更快,不過。PHP 7.3.7)
David at Weintraub.name
17 年前
文件中有一個錯誤

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

您也應該在文件中加入「popen」和「pclose」。我是一個 PHP 開發新手,並去驗證我是否可以在我使用「popen」的指令上使用「fgets」。
Peter Schlaile
17 年前
fscanf($file, "%s\n") 並不是 fgets() 的一個很好的替代品,因為它會在第一個空白字元處停止解析,而不是在行尾!

(有關此的詳細資訊,請參閱 fscanf 頁面)
tavernadelleidee[italy]
18 年前
我認為以相反順序讀取(長)檔案的最快方法是

<?php
$myfile
= 'myfile.txt';
$command = "tac $myfile > /tmp/myfilereversed.txt";
passthru($command);
$ic = 0;
$ic_max = 100; // 停止讀取檔案的最大行數
$handle = fopen("/tmp/myfilereversed.txt", "r");
while (!
feof($handle) && ++$ic<=$ic_max) {
$buffer = fgets($handle, 4096);
echo
$buffer."<br>";
}
fclose($handle);
?>

它會在讀取檔案時同時輸出每一行,因此適用於像是紀錄檔之類的大檔案。

Borgonovo (無需翻譯)
To Top