CSVではなくUnicodeテキストを読み込みたい
ExcelでShift_JIS外の文字を扱っている場合、Excelからcsv書き出しした時点で文字が?に置換されて消えてしまいます。保存形式を「Unicodeテキスト」にすると、文字が消えずUnicodeのタブ区切りテキスト(tsv)で書き出されます。
これをPHPで読み込むのに、意外とハマったのでメモです。
ソースコード
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Excelから保存したUnicodeテキストのアップロード</title>
</head>
<body>
<p>Excelから保存したUnicodeテキストのアップロード</p>
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="tsvfile">
<button type="submit">送信</button>
</form>
<p>
<?php
$err_msg = '';
//データを格納する配列
$tsvdata = array();
if (is_uploaded_file($_FILES["tsvfile"]["tmp_name"])) {
$file_tmp_name = $_FILES["tsvfile"]["tmp_name"];
$file_name = $_FILES["tsvfile"]["name"];
//文字コード指定
setlocale(LC_ALL, 'ja_JP.UTF-8');
//拡張子を判定
if (pathinfo($file_name, PATHINFO_EXTENSION) != 'txt') {
$err_msg = '拡張子txtのUnicodeテキストファイルのみ対応しています。';
} else {
//UTF-16からUTF-8へ変換
$buffer = file_get_contents($file_tmp_name);
file_put_contents($file_tmp_name, mb_convert_encoding($buffer, 'UTF-8', 'UTF-16'));
unset($buffer);
//変換がおわったら、ファイルを読み込む
$fp = fopen($file_tmp_name, "r");
//区切り文字にタブを指定して配列に格納していく
while (($data = fgetcsv($fp, 0, "\t")) !== FALSE) {
if(count($data)==4){
//カラム数が4列の場合のみデータに追加する
$tsvdata[] = $data;
}
}
fclose($fp);
}
//---処理終了---
if($err_msg == ''){
//簡易表示チェック
echo '<table border="1">';
for($cnt1=0;$cnt1<count($tsvdata);$cnt1++){
echo '<tr>';
for($cnt2=0;$cnt2<count($tsvdata[$cnt1]);$cnt2++){
echo '<td>'.nl2br($tsvdata[$cnt1][$cnt2]).'</td>';
}
echo '</tr>';
}
echo '</table>';
}else{
//エラーメッセージ表示
echo $err_msg;
}
}
?>
</p>
</body>
</html>
ポイント
- Excelから書き出す「Unicodeテキスト」の文字コードは「UTF-16」である。そして、UTF-16は、
mb_detect_encoding()
では判別できない。 -
setlocale(LC_ALL, 'ja_JP.UTF-8');
をしないと、一部の文字が表示されない現象があった。
参考にさせていただいた記事など
Qiita : PHPでCSVファイルをアップロードする(そして配列に変換)
Qiita : CSVアップロードからのMySQLへのデータ挿入
PHPマニュアル : mb_detect_order ※UTF-16はエンコーディング検出できない
さいごに
- たぶんエラー処理など甘い気がしますが、何か気づいた点などがあればご指摘ください。