iconv('GBK', 'UTF-8', ...) 轉碼後,fgetcsv() 讀取中文欄位回傳空字串,英文欄位正常。fgetcsv() 依賴 C 語言的 setlocale() 正確解析多位元組字元。Linux 預設 locale 為 C 或 POSIX(單位元組),無法正確識別 UTF-8 中文字元邊界,導致欄位切割錯誤、中文變空字串。
<?php
// 1. 上傳的 CSV 通常為 GBK/Big5,先轉 UTF-8
$content = file_get_contents($tmpFile);
$content = iconv('GBK', 'UTF-8//IGNORE', $content);
file_put_contents($tmpFile, $content);
// 2. 關鍵:設定 locale 為 UTF-8 中文環境
setlocale(LC_ALL, 'zh_CN.UTF-8'); // 或 'zh_TW.UTF-8'、'en_US.UTF-8' 皆可,關鍵是 .UTF-8
// 3. 開啟檔案並讀取
$handle = fopen($tmpFile, 'r');
$headers = fgetcsv($handle); // 現在中文欄位正常
while (($row = fgetcsv($handle)) !== false) {
// 處理每一行資料
}
fclose($handle);
?>
| 作業系統 | 中文 UTF-8 locale |
|---|---|
| Linux (Debian/Ubuntu/CentOS) | zh_CN.UTF-8、zh_TW.UTF-8 |
| FreeBSD | zh_CN.UTF-8 |
| macOS | zh_TW.UTF-8 |
可用 locale -a 指令查詢系統支援的 locale。
ini_set('auto_detect_line_endings', true); // PHP 4.3+
<?php
function readCsvRobust($filePath, $fromEncoding = 'GBK') {
// 讀取並轉碼
$content = file_get_contents($filePath);
$content = iconv($fromEncoding, 'UTF-8//IGNORE', $content);
$tmpFile = tempnam(sys_get_temp_dir(), 'csv_');
file_put_contents($tmpFile, $content);
// 設定 locale
$locales = ['zh_CN.UTF-8', 'zh_TW.UTF-8', 'en_US.UTF-8', 'C.UTF-8'];
$set = false;
foreach ($locales as $loc) {
if (setlocale(LC_ALL, $loc) !== false) {
$set = true;
break;
}
}
if (!$set) {
trigger_error('無法設定 UTF-8 locale,CSV 解析可能異常', E_USER_WARNING);
}
ini_set('auto_detect_line_endings', true);
$handle = fopen($tmpFile, 'r');
$headers = fgetcsv($handle);
$data = [];
while (($row = fgetcsv($handle)) !== false) {
$data[] = array_combine($headers, $row);
}
fclose($handle);
unlink($tmpFile);
return $data;
}
?>