CSV格式簡介
CSV 即 Comma Separate Values
,這種檔案格式經常用來作為不同程式之間的資料互動的格式。
格式特點
- 每條記錄佔一行,且記錄中欄位間以逗號
,
分割,行與行之間用換行符分割; - 逗號
,
前後的空格會被忽略; - 若記錄的欄位中含有逗號
,
,那麼久必須用雙引號包括起來; - 欄位中包含有換行符,該欄位必須用雙引號括起來;
- 欄位前後包含有空格,該欄位必須用雙引號括起來;
- 欄位中的雙引號用兩個雙引號表示;
- 欄位中如果有雙引號,該欄位必須用雙引號括起來;
- 第一條記錄,可以是欄位名,相當於表頭的位置資料。
普通拼接格式匯出
瀏覽器匯出
/**
* 匯出CSV檔案
*/
function exportCsv()
{
// 需要匯出的內容
$data = [
['name' => '張三', 'score' => '80'],
['name' => '李四', 'score' => '90'],
['name' => '王五', 'score' => '60'],
];
// 檔名,這裡都要將utf-8編碼轉為gbk,要不可能出現亂碼現象
$filename = $this->utfToGbk('匯出csv檔案.csv');
// 拼接檔案資訊,這裡注意兩點
// 1、欄位與欄位之間用逗號分隔開
// 2、行與行之間需要換行符
$fileData = $this->utfToGbk('姓名, 分數') . "\n";
foreach ($data as $value) {
$temp = $value['name'] . ',' .
$value['score'];
$fileData .= $this->utfToGbk($temp) . "\n";
}
// 頭資訊設定
header("Content-type:text/csv");
header("Content-Disposition:attachment;filename=" . $filename);
header('Cache-Control:must-revalidate,post-check=0,pre-check=0');
header('Expires:0');
header('Pragma:public');
echo $fileData;
exit;
}
/**
* 字元轉換(utf-8 => GBK)
*/
function utfToGbk($data)
{
return iconv('utf-8', 'GBK', $data);
}
通過命令列方式匯出資料
/**
* 下載CSV檔案
*/
public function downLoadCsv()
{
// 需要匯出的內容
$data = [
['name' => '張三', 'score' => '80'],
['name' => '李四', 'score' => '90'],
['name' => '王五', 'score' => '60'],
];
// 檔名,這裡都要將utf-8編碼轉為gbk,要不可能出現亂碼現象
$filename = $this->utfToGbk('生成csv檔案.csv');
// 拼接檔案資訊,這裡注意兩點
// 1、欄位與欄位之間用逗號分隔開
// 2、行與行之間需要換行符
$fileData = $this->utfToGbk('姓名, 分數') . "\n";
foreach ($data as $value) {
$temp = $value['name'] . ',' .
$value['score'];
$fileData .= $this->utfToGbk($temp) . "\n";
}
$filePath = __DIR__ . '/' . $filename;
// 將一個字串寫入檔案
file_put_contents($filePath, $fileData);
return $filePath;
}
/**
* 字元轉換(utf-8 => GBK)
*/
public function utfToGbk($data)
{
return iconv('utf-8', 'GBK', $data);
}
注意:
上述匯出方式中,匯出的資料格式均是採用程式碼拼接而成。
在實踐中,發現這種匯出方式在遇到一些長形文字尤其是其中含有特殊字元的,就會導致一個欄位中的文字在匯出的 CSV
檔案中佔據好幾行的空間,顯示錯亂。
因此,更推薦下面的 fputcsv 函式匯出。
fputcsv 函式匯出
fputcsv ( resource
$handle
, array$fields
[, string$delimiter
= ‘,’ [, string$enclosure
= ‘“‘ ]] ) : int
fputcsv — 將行格式化為 CSV 並寫入檔案指標。
fputcsv 將一行(用 fields
陣列傳遞)格式化為 CSV 格式並寫入由 handle
指定的檔案。
引數
handle
必選引數,檔案指標必須是有效的,必須指向由 fopen() 或 fsockopen() 成功開啟的檔案(並還未由 fclose() 關閉)。
fields
必選引數,儲存一行各欄位資料的陣列。
delimiter
可選引數,
delimiter
引數設定欄位分界符(只允許一個字元),也就是每行欄位之間分界符,如逗號,
。enclosure
可選引數,
enclosure
引數設定欄位欄位環繞符(只允許一個字元),如雙引號""
。
返回值
成功時,返回寫入字串的長度, 或者在失敗時返回 FALSE
。
示例
$list = array (
array('aaa', 'bbb', 'ccc', 'dddd'),
array('123', '456', '789'),
array('"aaa"', '"bbb"')
);
$fp = fopen('file.csv', 'w');
foreach ($list as $fields) {
fputcsv($fp, $fields);
}
fclose($fp);
輸出:
aaa,bbb,ccc,dddd
123,456,789
"""aaa""","""bbb"""
通過命令列方式匯出資料
// 按教師作文號碼 匯出大賽資料
function exportDataByRid($dir,$ridArr){
$arr = [];
foreach($ridArr as $k => $rid) {
$essayList = $this->db()->getAll("select user_id,essay_id,title,essay,score,stu_number,stu_class from eng_essay where request_id = $rid and type >= 0");
$user_ids = array_column($essayList, 'user_id');
$user_ids = array_unique($user_ids);
$userInfos = $this->member()->getMembersByIds($user_ids, ['name','school']);
// 存放標準資料的陣列
$dataList = [];
// 遍歷原陣列,重組順序
foreach($essayList as $key => $essay) {
$dataList[$key]['user_id'] = $essay['user_id'];
$dataList[$key]['name'] = $userInfos[$essay['user_id']]['name'];
$dataList[$key]['school'] = $userInfos[$essay['user_id']]['school'];
$dataList[$key]['stu_class'] = $essay['stu_class'];
$dataList[$key]['stu_number'] = $essay['student_number'];
$dataList[$key]['essay_id'] = $essay['essay_id'];
$dataList[$key]['title'] = $essay['title'];
$dataList[$key]['essay'] = $essay['essay'];
$dataList[$key]['score'] = $this->score($essay['score']);
}
$filename = $this->utfToGbk($rid . '.csv');
// csv 檔案的頭部
$fileData = $this->utfToGbk('使用者ID,姓名,學校,班級,學號,作文號,題目,內容, 分數') . "\n";
if( !is_dir( $dir) ){
if( !mkdir($dir )) return array('error'=>'目錄不不可寫!');
}
$filePath = $dir.$filename;
// 生成 檔案 handler
$file = fopen($filePath,"w");
fputcsv($file, explode(',', $fileData));
//計數器
$num = 0;
//每隔$limit行,重新整理一下輸出buffer,不要太大,也不要太小
$limit = 100000;
foreach ($dataList as $value) {
$num++;
//重新整理一下輸出buffer,防止由於資料過多造成問題
if ($limit == $num) {
ob_flush();
flush();
$num = 0;
}
array_walk($value, function (&$val, $key){
$val = $this->utfToGbk($val);
});
fputcsv($file, $value);
}
// 記得關閉
fclose($file);
$arr[] = $filePath;
}
print_r($arr);
}
/**
* 字元轉換(utf-8 => GBK)
*/
public function utfToGbk($data)
{
return iconv('utf-8', 'GBK', $data);
}
注意
優點
這種情況下,即使長文字欄位中也不會出現佔行錯亂的問題,如 上述程式碼中的 essay
欄位。
缺陷
但是還有一個問題,就是當欄位內容在使用 iconv
函式進行 GBK 轉換(如上述程式碼中的 iconv('utf-8', 'GBK', $data);
)時,存在無法轉換的內容或者特殊字元,那麼轉換過程中就會報錯,從而導致在匯出的 CSV 中 整個欄位內容的丟失。
解決方案
解決上述整個欄位內容的丟失的問題,我們需要在使用 iconv
轉化欄位內容字符集時,在目的字符集的後面新增上 //IGNORE
標示,使得在轉換過程中遇到錯誤時,忽略錯誤,接著執行,最終獲得欄位完整內容,從而順利匯出。
/**
* 字元轉換(utf-8 => GBK)
*/
public function utfToGbk($data)
{
return iconv('utf-8', 'GBK//IGNORE', $data);
}
參考連結
本作品採用《CC 協議》,轉載必須註明作者和本文連結