PHP 提供多種檔案讀取方式,最常見的是 fopen() 串流處理與 file_get_contents() 一次性讀取。本文詳細比較兩者差異、記憶體影響、大檔案處理策略,並提供最佳實踐建議。
<?php
$filePath = '/var/log/nginx/access.log'; // 可能數 GB 大小
$handle = fopen($filePath, 'rb');
if ($handle === false) {
throw new RuntimeException("無法開啟檔案:$filePath");
}
$lineNumber = 0;
while (($line = fgets($handle)) !== false) {
$lineNumber++;
// 處理單行資料,例如解析日誌、過濾關鍵字
if (strpos($line, 'ERROR') !== false) {
echo "第 $lineNumber 行: $line";
}
// 記憶體佔用極低,僅保留當前行
}
if (!feof($handle)) {
throw new RuntimeException("讀取檔案時發生錯誤:$filePath");
}
fclose($handle);
// 進階:使用 SplFileObject 物件導向介面
$file = new SplFileObject($filePath, 'r');
$file->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY);
foreach ($file as $lineNumber => $line) {
// $line 已自動去除換行符 (視旗標而定)
// 可用 $file->fgetcsv() 直接解析 CSV
}
?>
<?php
$filePath = '/etc/nginx/nginx.conf'; // 通常 < 1MB
if (file_exists($filePath)) {
$content = file_get_contents($filePath);
if ($content === false) {
throw new RuntimeException("讀取失敗:$filePath");
}
echo $content; // 或進行字串處理
} else {
echo "檔案不存在:$filePath";
}
// 進階:串流上下文控制 (HTTP 請求、限制長度、偏移量)
$context = stream_context_create([
'http' => [
'method' => 'GET',
'header' => 'User-Agent: MyApp/1.0',
'timeout' => 10,
],
'ssl' => ['verify_peer' => false, 'verify_peer_name' => false], // 僅測試環境
]);
$remoteContent = file_get_contents('https://api.example.com/data', false, $context);
// 讀取檔案部分內容 (PHP 7.1+ 支援 offset/length 參數)
$chunk = file_get_contents($filePath, false, null, 0, 1024); // 讀取前 1KB
?>
| 維度 | fopen() + fgets() | file_get_contents() |
|---|---|---|
| 記憶體佔用 | 極低 (單行緩衝) | 等於檔案大小 (一次載入) |
| 適用檔案大小 | 無上限 (GB 級) | 建議 < 10MB (視 memory_limit) |
| 處理速度 | 較慢 (多次系統呼叫) | 極快 (單次系統呼叫) |
| 程式碼複雜度 | 較高 (需手動迴圈、錯誤處理) | 極低 (一行搞定) |
| 隨機存取 | 支援 fseek() | PHP 7.1+ 支援 offset/length |
| 遠端 URL 讀取 | 需配合 stream_context | 原生支援 (allow_url_fopen) |
| 二進位安全 | 是 (rb 模式) | 是 |
<?php
$lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($lines === false) { /* 錯誤處理 */ }
foreach ($lines as $line) {
// 逐行處理
}
// 注意:file() 同樣會將整個檔案載入記憶體,不適合大檔案
?>
fopen() / SplFileObject 串流處理。stream_set_read_buffer($handle, 0) 關閉緩衝可進一步降低記憶體峰值。pcntl_fork / Swoole 多進程並行處理。ini_set('memory_limit', '512M'),但串流處理通常不需調高。while (!feof($handle)) 迴圈在最後一行讀取後仍會執行一次,導致處理空行。正確寫法:while (($line = fgets($handle)) !== false)。mb_convert_encoding($line, 'UTF-8', 'Big5')。fgets() 保留 \r\n,處理時建議 rtrim($line, "\r\n")。allow_url_fopen=Off,遠端請求改用 cURL 或 Guzzle,安全性更高。$filesystem->readStream($path)。Finder::create()->files()->in($dir)->depth(0)。file_get_contents 讀取遠端資源。更多 PHP 檔案系統操作請參考:《PHP SplFileObject 進階應用》、《PHP CSV 大量匯入優化策略》、《PHP 串流與過濾器深度解析》。選擇正確的讀取方式是高效處理檔案資料的關鍵。