CSV 是資料交換常用格式。本文提供一個功能完整的 CsvToArray 類別,支援自動偵測分隔符、標題列對應欄位名、第一列資料不遺漏、多維陣列依指定欄位排序,適合匯入會員、訂單、商品等批次資料處理。
CsvToArray<?php
final class CsvToArray {
/**
* 讀取 CSV 並回傳關聯陣列
* @param string $file 檔案路徑
* @param string $delimiter 分隔符 (預設 ;)
* @return array 二維關聯陣列 [row][欄位名] = 值
*/
public static function open($file, $delimiter = ';') {
return self::sortMultiArray(self::parseCsv($file, $delimiter), 1);
}
/**
* 解析 CSV 為關聯陣列
*/
private static function parseCsv($file, $delimiter) {
$result = [];
$size = filesize($file) + 1;
$handle = fopen($file, 'r');
if (!$handle) throw new Exception("無法開啟檔案: $file");
// 讀取標題列
$headers = fgetcsv($handle, $size, $delimiter);
if (!$headers) throw new Exception("CSV 空檔或無標題列");
// 關鍵:指標歸零,確保第一列資料不被跳過
fseek($handle, 0);
while (($row = fgetcsv($handle, $size, $delimiter)) !== false) {
$assoc = [];
foreach ($row as $i => $value) {
// 以標題名為鍵,同時保留數字索引
if (isset($headers[$i])) {
$assoc[$headers[$i]] = $value;
}
$assoc[$i] = $value;
}
$result[] = $assoc;
}
fclose($handle);
return $result;
}
/**
* 多維陣列依指定欄位索引排序
* @param array $multiArray
* @param int $sortKeyIndex 排序依據的欄位索引 (0-based)
* @return array
*/
private static function sortMultiArray($multiArray, $sortKeyIndex) {
$indexMap = [];
foreach ($multiArray as $idx => $row) {
$indexMap[$idx] = $row[$sortKeyIndex] ?? '';
}
asort($indexMap, SORT_STRING | SORT_FLAG_CASE);
$sorted = [];
foreach ($indexMap as $idx => $_) {
$sorted[] = $multiArray[$idx];
}
return $sorted;
}
}
?>
假設 test.csv 內容 (分隔符為 ;):
學號;姓名;年齡;身高
A001;王小明;18;175
A002;李小美;17;162
A003;張大華;19;180
<?php
require 'CsvToArray.class.php';
try {
$data = CsvToArray::open('test.csv', ';');
echo "";
print_r($data);
echo "";
// 逐列輸出
foreach ($data as $row) {
echo "學號:{$row[0]} / {$row['學號']}
";
echo "姓名:{$row[1]} / {$row['姓名']}
";
echo "年齡:{$row[2]} / {$row['年齡']}
";
echo "身高:{$row[3]} / {$row['身高']}
";
}
} catch (Exception $e) {
echo "錯誤:" . $e->getMessage();
}
?>
Array
(
[0] => Array
(
[0] => A001
[學號] => A001
[1] => 王小明
[姓名] => 王小明
[2] => 18
[年齡] => 18
[3] => 175
[身高] => 175
)
[1] => Array
(
[0] => A002
[學號] => A002
[1] => 李小美
[姓名] => 李小美
[2] => 17
[年齡] => 17
[3] => 162
[身高] => 162
)
[2] => Array
(
[0] => A003
[學號] => A003
[1] => 張大華
[姓名] => 張大華
[2] => 19
[年齡] => 19
[3] => 180
[身高] => 180
)
)
| 需求 | 實作方向 |
|---|---|
| 支援 UTF-8 BOM | 讀取前 3 位元組判斷 EF BB BF,若存在則 fseek(3) |
| 自動偵測分隔符 | 掃描第一行,統計 , ; \t | 出現次數,取最大者 |
| 大檔案分塊處理 | 實作 Generator 逐行 yield,避免一次載入全部記憶體 |
| 編碼自動轉換 | mb_detect_encoding + mb_convert_encoding 統一轉 UTF-8 |
| 資料型別轉換 | 依標題名稱規則 (如 _int, _date, _bool) 自動轉型 |
| 驗證規則 | 整合 respect/validation 等套件,逐行驗證並收集錯誤 |