PHP 匯出 CSV 時常遇到兩大問題:
00001 解析為 1本文提供生產級解決方案,核心技巧:在欄位值前加入 \t (Tab) 字元強制 Excel 視為文字,搭配 mb_convert_encoding 轉 Big5/GB2312 編碼。
<?php
/**
* 產生 CSV 並輸出下載
* @param array $lists 資料集合
* @param string $filename 檔名稱
*/
function exportCsv(array $lists, string $filename = '') {
// 1. 定義標題列
$header = '規則111,規則222,審222,規222,服2222,規則1,規則2,規則3,匹配字符,設置時間,有效期';
$exportData = $header . "\n";
// 2. 組裝資料列(關鍵:每欄位以 "\t" 開頭強制文字格式)
if (!empty($lists)) {
foreach ($lists as $value) {
$exportData .= '"' . "\t" . $value['Rule_id'] . '",'
. '"' . "\t" . $value['Rule_name'] . '",'
. '"' . "\t" . $value['Matching_level'] . '",'
. '"' . "\t" . $value['Rule_action'] . '",'
. '"' . "\t" . $value['Service_type'] . '",'
. '"' . "\t" . $value['Keyword1'] . '",'
. '"' . "\t" . $value['Keyword2'] . '",'
. '"' . "\t" . $value['Keyword3'] . '",'
. '"' . "\t" . $value['Matching_word'] . '",'
. '"' . "\t" . $value['Set_time'] . '",'
. '"' . "\t" . $value['Validation_time'] . '"' . "\n";
}
}
// 3. 設定下載標頭
$filename = $filename ?: "plcnetinfo_" . date("YmdHis") . ".csv";
header("Content-Type: application/vnd.ms-excel; charset=gb2312");
header("Content-Disposition: attachment; filename=\"{$filename}\"");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Pragma: public");
// 4. 輸出前轉碼(UTF-8 -> GB2312/Big5 相容中文 Excel)
echo mb_convert_encoding($exportData, "gb2312", "UTF-8");
}
// 使用範例
$sql = "SELECT * FROM rules WHERE status = 1";
$lists = $db->query($sql); // 假設 PDO 查詢結果
exportCsv($lists);
?>
| 技巧 | 作用 | 說明 |
|---|---|---|
"\t" 前綴 |
保留前導零/長數字 | Excel 偵測到 Tab 字元會將該欄位視為純文字,不進行數值轉換 |
| 雙引號包裹 | 處理逗號/換行 | 欄位內含逗號、雙引號、換行時不會破壞 CSV 結構 |
mb_convert_encoding |
解決中文亂碼 | 中文版 Excel 預設開啟 CSV 使用系統編碼,UTF-8 會亂碼 |
header() 設定 |
觸發瀏覽器下載 | Content-Disposition: attachment 強制下載而非瀏覽器開啟 |
<?php
class CsvExporter {
private $data = [];
private $headers = [];
private $forceTextColumns = []; // 強制文字欄位索引
public function setHeaders(array $headers) { $this->headers = $headers; }
public function setData(array $data) { $this->data = $data; }
public function setForceTextColumns(array $indices) { $this->forceTextColumns = $indices; }
public function download(string $filename = '') {
$output = fopen('php://temp', 'r+');
// 寫入標題
fputcsv($output, $this->headers);
// 寫入資料
foreach ($this->data as $row) {
$formatted = [];
foreach ($row as $idx => $val) {
if (in_array($idx, $this->forceTextColumns)) {
$formatted[] = "\t" . $val; // 強制文字
} else {
$formatted[] = $val;
}
}
fputcsv($output, $formatted);
}
rewind($output);
$csvContent = stream_get_contents($output);
fclose($output);
// 輸出下載(同前)
header("Content-Type: application/vnd.ms-excel; charset=gb2312");
header("Content-Disposition: attachment; filename=\"{$filename}\"");
echo mb_convert_encoding($csvContent, "gb2312", "UTF-8");
}
}
// 使用方式
$exporter = new CsvExporter();
$exporter->setHeaders(['ID', '姓名', '工號', '電話']);
$exporter->setData($userList);
$exporter->setForceTextColumns([2, 3]); // 工號、電話保留前導零
$exporter->download("users_" . date("Ymd") . ".csv");
?>
匯入時若欄位前有 "\t",請使用 trim() 清除:
$cleanValue = trim($rawValue, "\t\r\n\" ");
\t 前綴法相容性最佳,支援 Excel、WPS、LibreOffice Calcfputcsv() 串流寫入,避免記憶體耗盡header() 之後、echo 之前執行filename*=UTF-8''%E6%AA%94%E6%A1%88.csv