いくつかの文字コードのCSVファイルの読み込みに対応した話
ソリューションsecの由良です
CSVファイルを読み込んでデータを登録する処理というのを結構作ることが多いんですが、そのCSVファイルはCSVファイルを作って登録する側からしたら文字コードのことはわからなくてCSVなら取り込めると思います。
CSVファイルの文字コードが異なると一つの文字コードだけ受け入れるプログラムの処理の場合文字化けして取り込もうとしてエラーになることがあります。
文字コードの取り決めがしっかりしているならfgetcsv()などで1行ずつ読み込んで処理すれば大丈夫ですが、EXCELなどでCSVファイルを作った場合は大体SJIS-winで保存されます。サーバーではUTF-8で処理することが多いので変更しないといけません。
//$lineは1行の配列、$line[0]は最初のデータ
if (mb_detect_encoding($line[0]) == "SJIS-win") {
mb_convert_variables("UTF-8", "SJIS-win", $line);
}
読み込んだ行の最初がSJIS-winなら変換します。
上記の内容で大体行けると思うのですが内容を比較したりするときにうまくいかない場合があってその時は
//$lineは1行の配列、$line[0]は最初のデータ
$encode = mb_detect_encoding($line[0], "UTF-8, JIS, eucjp-win, sjis-win");
if ($encode != "UTF-8") {
mb_convert_variables("UTF-8", "SJIS-win", $line);
}
エンコードを確認したうえで変換したりする場合があります。これでSJIS-winの変換はできます。
UTF-8にはBOM(Byte Order Mark」)Unicode(統一文字コード)の符号化形式で符号化したテキストの先頭に数バイトのデータが付与されていることがあります(Windowsで文字化けしないように追加)。BOMあった場合の先頭データはBOMがついたデータになるので、プログラムの判定処理に失敗します。BOMがあった場合の処理も必要です。
そのBOMデータは16進数で 0xEF 0xBB 0xBF
3バイトあるのでこれを切り取ってあげる必要があります
//$line[0]は1行の最初のデータ
if (preg_match('/\A\xEF\xBB\xBF/', $csvData[0])) {
$line[0] = preg_replace('/\A\xEF\xBB\xBF/', '', $line[0]);
}
一番最初の先頭データのみBOMが追加されるので1行目の先頭だけ処理してあげればOKです。
CSVを読み込むときはこのあたりの処理はついて回るので共通化して使いまわしたいところです。