基本方法:fopen + fgets 迴圈
<?php
$file = fopen('users.txt', 'r');
if (!$file) die('無法開啟檔案');
$users = [];
while (($line = fgets($file)) !== false) {
$line = rtrim($line, "\n\r"); // 移除換行符
if ($line !== '') { // 略過空行
$users[] = $line;
}
}
fclose($file);
print_r($users);
// Array ( [0] => user01 [1] => user02 ... )
?>
進階:Generator 逐行產生(極低記憶體)
<?php
function readLines(string $filename): Generator {
$fh = fopen($filename, 'r');
if (!$fh) throw new RuntimeException("Cannot open $filename");
try {
while (($line = fgets($fh)) !== false) {
yield rtrim($line, "\n\r");
}
} finally {
fclose($fh);
}
}
// 使用方式:逐行處理,不一次載入全部
foreach (readLines('huge_log.txt') as $line) {
if (str_contains($line, 'ERROR')) {
processError($line);
}
}
// 記憶體佔用恆定,無論檔案多大
?>
常用便捷函數對比
| 函數 | 行為 | 記憶體 | 適用場景 |
file($file) | 一次讀全檔 → 陣列(每行含換行符) | 高(整檔載入) | 小檔案、需隨機存取行號 |
file_get_contents($file) | 一次讀全檔 → 字串 | 高 | 需整體字串處理(正則、JSON 解析) |
fgets($fh) | 逐行讀取 | 低(單行緩衝) | 大檔案、串流處理、記憶體受限 |
SplFileObject | 物件導向逐行迭代 | 低 | 需面向物件、可配合 FilterIterator |
| Generator + fgets | 逐行產生 | 極低 | 超大檔案、管線處理、併發友善 |
SplFileObject 範例
<?php
$file = new SplFileObject('data.csv');
$file->setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY | SplFileObject::READ_AHEAD);
foreach ($file as $row) {
// $row 已自動解析為陣列
[$id, $name, $email] = $row;
// 處理...
}
?>
編碼處理:非 UTF-8 檔案
<?php
// Big5/GBK 檔案轉 UTF-8 逐行讀取
$fh = fopen('big5.txt', 'r');
while (($line = fgets($fh)) !== false) {
$line = iconv('BIG5', 'UTF-8//IGNORE', $line);
// 處理 $line...
}
fclose($fh);
?>
效能測試參考(100MB 檔案,100萬行)
| 方法 | 時間 | 峰值記憶體 |
file() | 1.2s | ~280 MB |
fgets() 迴圈 | 0.9s | ~2 MB |
| Generator | 0.9s | ~1 MB |
SplFileObject | 1.0s | ~2 MB |
最佳實踐建議
- 檔案 < 10MB:
file() 或 file_get_contents() 最簡潔
- 檔案 > 10MB 或記憶體受限:
fgets() 迴圈或 SplFileObject
- 資料管線/轉換/過濾:Generator 最靈活,可串聯
array_filter、array_map 風格處理
- 永遠
fclose() 或用 try/finally 確保釋放檔案控制代碼
- CSV/TSV 結構化資料 →
SplFileObject::READ_CSV 內建解析