大文件(GB 級)不可一次性 file_get_contents 載入內存,必須**流式讀取**。本文提供三類實用函數:fgets 逐行讀取、fseek+SEEK_END 讀取最後 N 行、分塊緩衝 高性能尾部讀取。
$fp = fopen('huge.log', 'r');
while (($line = fgets($fp)) !== false) {
// 處理單行 $line
// 內存佔用僅為單行大小
}
fclose($fp);
/**
* 返回文件最後 $n 行字符串
*/
function FileLastLines($filename, $n)
{
if (!$fp = fopen($filename, 'r')) return false;
$pos = -2; $eof = ''; $str = '';
while ($n > 0) {
while ($eof != "\n") {
if (!fseek($fp, $pos, SEEK_END)) {
$eof = fgetc($fp);
$pos--;
} else { break; } // 到達文件開頭
}
$str .= fgets($fp);
$eof = ''; $n--;
}
fclose($fp);
return $str;
}
echo nl2br(FileLastLines('sss.txt', 4));
function tail($fp, $n, $base = 5)
{
assert($n > 0);
$pos = $n + 1;
$lines = [];
while (count($lines) <= $n) {
try {
fseek($fp, -$pos, SEEK_END);
} catch (Exception $e) {
fseek($fp, 0);
break;
}
$pos *= $base; // 指數增大讀取塊
while (!feof($fp)) {
array_unshift($lines, fgets($fp));
}
}
return array_slice($lines, 0, $n);
}
// 使用
$lines = tail(fopen("access.log", "r"), 10);
var_dump($lines);
$fp = fopen($file, "r");
$line = 10; $pos = -2; $t = " "; $data = "";
while ($line > 0) {
while ($t != "\n") {
fseek($fp, $pos, SEEK_END);
$t = fgetc($fp);
$pos--;
}
$t = " ";
$data .= fgets($fp);
$line--;
}
fclose($fp);
echo $data;
| 場景 | 推薦方法 | 特點 |
|---|---|---|
| 逐行處理日誌/CSV | fgets() 循環 | 簡單、穩定、內存 O(1) |
| 獲取最後 N 行(中小文件) | FileLastLines() | 代碼少、易理解 |
| 獲取最後 N 行(超大文件、高頻) | tail() 分塊緩衝 | 減少 fseek 系統調用、性能最優 |
\r\n 可能導致 $eof != "\n" 判斷失效,建議用 trim() 或檢測 PHP_EOL。fseek($fp, -$offset, SEEK_END) 偏移量超過文件大小會報錯,需 try-catch 或檢查返回值。