CSV(Comma-Separated Values)是資料交換最通用格式。PHP 內建 fgetcsv() 與 SplFileObject 兩大工具,本文涵蓋全量讀取、指定行讀取、區間讀取、大檔案記憶體優化、編碼處理等實戰場景。
<?php
$file = fopen('data.csv', 'r');
if (!$file) die('無法開啟檔案');
$allData = [];
while (($row = fgetcsv($file)) !== false) {
$allData[] = $row; // 每行為一個索引陣列
}
fclose($file);
// 存取範例:第 3 行第 1 欄(索引從 0 開始)
echo $allData[2][0];
?>
<?php
function getCsvLine($filename, $lineNumber) {
$handle = fopen($filename, 'r');
if (!$handle) return false;
$currentLine = 0;
while (!feof($handle)) {
$row = fgetcsv($handle);
if ($row === false) continue; // 空行
$currentLine++;
if ($currentLine === $lineNumber) {
fclose($handle);
return $row;
}
}
fclose($handle);
return false;
}
$row10 = getCsvLine('data.csv', 10);
print_r($row10);
?>
<?php
function getCsvRange($filename, $startLine, $endLine) {
$handle = fopen($filename, 'r');
if (!$handle) return false;
$result = [];
$currentLine = 0;
while (!feof($handle)) {
$row = fgetcsv($handle);
if ($row === false) continue;
$currentLine++;
if ($currentLine < $startLine) continue;
if ($currentLine > $endLine) break;
$result[] = $row;
}
fclose($handle);
return $result;
}
// 讀取第 11-20 行
$page = getCsvRange('data.csv', 11, 20);
foreach ($page as $row) {
echo implode(', ', $row) . "\n";
}
?>
內建 Iterator 介面,可直接 foreach、seek() 定位、setCsvControl() 自訂分隔符。
<?php
$file = new SplFileObject('data.csv');
$file->setFlags(SplFileObject::READ_CSV); // 以 CSV 模式讀取
$file->setCsvControl(','); // 分隔符,預設逗號
foreach ($file as $lineNum => $row) {
// $lineNum 從 0 開始
if ($lineNum === 0) continue; // 跳過標題行
echo "Row $lineNum: " . implode(' | ', $row) . "\n";
}
?>
<?php
$file = new SplFileObject('data.csv');
$file->setFlags(SplFileObject::READ_CSV);
$file->seek(9); // 移動到第 10 行(索引 9)
$row = $file->current();
print_r($row);
?>
<?php
$file = fopen('big5.csv', 'r');
while (($row = fgetcsv($file)) !== false) {
$row = array_map(function($cell) {
return mb_convert_encoding($cell, 'UTF-8', 'BIG-5');
}, $row);
// 處理 $row...
}
fclose($file);
?>
fgetcsv() 正確支援雙引號包裹的跨行欄位:
"欄位1","跨行
內容","欄位3"
無需額外處理,但確保 fgetcsv() 的 enclosure 參數為預設 `"`。
<?php
// fgetcsv
fgetcsv($handle, 0, "\t"); // Tab 分隔
// SplFileObject
$file->setCsvControl(';'); // 分號分隔
?>
<?php
function csvRows($filename) {
$handle = fopen($filename, 'r');
try {
while (($row = fgetcsv($handle)) !== false) {
yield $row; // 惰性產生,記憶體常數級
}
} finally {
fclose($handle);
}
}
// 使用:只載入當前行到記憶體
foreach (csvRows('huge.csv') as $row) {
// 處理單行
}
?>
<?php
$file = new SplFileObject('data.csv');
$file->setFlags(SplFileObject::READ_CSV | SplFileObject::READ_AHEAD);
$headers = $file->current(); // 第一行
$file->next();
foreach ($file as $row) {
$record = array_combine($headers, $row);
// $record['email'], $record['name'] ...
}
?>
| 場景 | 推薦方案 | 理由 |
|---|---|---|
| 小檔案(< 10MB)、需全量陣列 | fgetcsv 迴圈 | 簡單、相容所有 PHP 版本 |
| 中大檔案、需隨機存取/分頁 | SplFileObject::seek() | O(1) 定位、物件導向 |
| 超大檔案(GB 級)、串流處理 | Generator + fgetcsv | 記憶體常數、可中斷恢復 |
| 需自訂分隔符、編碼轉換 | 兩者皆可 | setCsvControl() / 手動 mb_convert_encoding |
PHP 讀取 CSV 核心在於 fgetcsv()(程序式)與 SplFileObject(物件式)兩大工具。掌握流式讀取避免記憶體爆炸、編碼轉換、RFC 4180 標準相容、生成器模式四大關鍵,即能應對從小型匯入到海量資料遷移的所有場景。生產環境建議統一封裝為 CsvReader 服務類,集中處理異常、日誌、編碼偵測等通用邏輯。