system
マリンロード

2016.5.16システム開発 

マリンロード

[PHP] SJISのCSVファイルの文字化けせずに読み込む方法(SplFileObject)

こんにちは。エンジニアの高田です。
今年のゴールデンウィーク驚異的な10連休でした!
やっとGWボケが抜けつつあります。

先日はまったPHPでのCSVファイル読込時の文字化け問題。。。
いや文字化けではなく項目の区切りがうまく判断されずに、隣りの項目とくっついちゃう現象。。。

どんな現象?

CSVファイルは「SJIS」で「タブ区切り」、「囲み文字(ダブルクォテーション)つき」でした。
PHPの文字コードは「UTF-8」

分かりやすく、「タブ区切り」を「カンマ区切り」にして表現すると
「”ID”, “代表者”, “代表者カナ”, “電話番号”」
 ↓ 読み込むと。。。
「”ID”, “代表者,代表者カナ”, “電話番号”」

なぜか2つ目と3つ目の項目がくっついちゃう。。。
ほとんどの場合はうまくいくのに。。。CSVを「UTF-8」にするとうまくいく。
「代表」って文字がいけないの?

文字化けの原因は?(SJIS)

文字化けの主な原因はCSVの文字コードとPHPの文字コードが異なるために起こります。
PHPでCSVファイルを扱うのに普通は「fgetcsv」関数を使用します。
「fgetcsv」は文字化けするというのを聞いたことがあったので、最近ではいつもPHP5.1から使用できる「SplFileObject」クラスを使用していました。
そして「SplFileObject」クラスで取得した文字列を「mb_convert_encoding 」関数あるいは「mb_convert_variables」関数を使用して「SJIS」から「UTF-8」に文字コードを変換していました。
ほとんどの場合はうまくいくのに。。。
やはり「SplFileObject」クラスも「SJIS」を正しく扱えないようです。

文字化けの対処方法(SplFileObject)

「SplFileObject」クラスで「SJIS」を正しく扱えないなら「UTF-8」にしてしまえ!ということで、対処方法は以下の通り。

CSVファイルを「file_get_contents」関数で一度読み込み、「mb_convert_encoding 」関数で文字コードを「UTF-8」に変換する。
それからテンポラリファイルを作成し、「UTF-8」に変換したデータを書き込む。
書き込んだテンポラリファイルの、ファイルポインタを先頭に戻し、「SplFileObject」クラスでいつも通り読み込みを行う。

 


  $strFilePath = 'C:\';
  $strFileName = 'Test.csv';
  $aryDataList = array();

  //--------------------------------------------------
  // 文字コードを変換した一時ファイルの作成
  //--------------------------------------------------
  // ファイルの読み込み
  $data = file_get_contents($strFilePath. $strFileName);
  // 文字コードの変換(UTF-8 → SJIS-win)
  $data = mb_convert_encoding($data, 'UTF-8', 'SJIS-win');
  // 一時ファイルの作成
  $temp = tmpfile();
  // メタデータからファイルパスを取得して読み込み
  $meta = stream_get_meta_data($temp);
  // 一時ファイル書き込み
  fwrite($temp, $data);
  // ファイルポインタの位置を先頭に
  rewind($temp);

  //--------------------------------------------------
  // ファイルの読み込み
  //--------------------------------------------------
  // CSVファイルの読み込み
  $objFile = new SplFileObject($meta['uri'], 'rb');
  $objFile->setFlags(SplFileObject::READ_CSV);
  $objFile->setCsvControl("\t", "\"");

  //--------------------------------------------------
  // データの取得
  //--------------------------------------------------
  foreach ($objFile as $aryData) {
    $aryDataList[] = $aryData;
  }

  fclose($temp);
  $objFile = null;

 
調べればこの方法が結構出てくるんですよ。
CSVファイルのデータが多いと、一度変換して保存し直すのは時間がかかるのではないかと思ったので、数万件のデータでテストしてみましたがこの変換部分は一瞬でした。
すべて文字コードが「UTF-8」になればいいのに。

ではまた。

システム開発サービスはこちら
ページTOPへ