PHPでCSVを読み込んだら配列に異常が出ていた件

PHPでCSVを読み込んだら配列に異常が出ていた件

この問題知りませんでした。既知の問題なのだそうです。

SJISのCSVをfgetcsvで直接読み取るのは「5C問題」のため危険である。

発生した異常

楽天市場の商品分析をCSVで落とし、PHPで読込み配列化、欲しい情報をDBに格納すると言う使い方をしたのですが、レコードをよく見ると意味不明なものが...。

6列目の値には出品URLが格納されているはずなのに【 0 】というレコードが存在する

楽天市場からDLされたデータの文字コードは Shift_jis サーバー側は UTF-8 なのでこの文字コードの問題だろうと思い、行ごとに読込んでいるパートでエンコードを変更、再度実行してみます。

...結果が変わらない。
まずい、これはハマるルートかも...。

まずは何が起こっているのか確認

可能性としてはこの3つしか考えられない

  1. 商品名等にカンマが入っていてCSVの読込場所がずれてしまった
  2. 使えない文字がある
  3. エンコードの変換が上手くいっていない

まずはエンコード変換結果の確認。
意味不明な文字は入っていないのでエンコードは成功しているようです。
カンマも入っていない。
となると使えない文字??

使用していた文字を切断してテストしてみた所、次の現象を確認しました。

文字列の最後に【能】の文字が入っていると次のカラムの値が 0 になる

fgetcsvでのCSV読込では『5C問題』が発生する

丁度【可能】って書いてあるところがダメだったんですよね。■5C問題とは

「ソ」「表」「十」「予」などSJISの文字の2バイト目が「5C」になる文字が、fgetcsvなどの処理機に与えられたとき、それが文字の一部としてでなく、ASCIIのバックスラッシュ(5C)と判断してしまい、エスケープ文字として解釈される問題を言う。

なるほど「能」もここに入るのね。
他にもこんな文字が5C問題を引き起こすようです。

— ソ Ы 噂 浬 欺 圭 構 蚕 十 申 曾 箪 貼 能 表 暴 予 禄 兔 喀 媾 彌 拿 杤 歃 濬 畚 秉 綵 臀 藹 觸 軆 鐔 饅 鷭

安全に処理する方法

次の段取りで操作を行うと解消されます。

  1. file_get_contentsでSJISで書かれたCSVファイルを読み込む
  2. 読込んだファイルをUTF-8にエンコードする
  3. エンコードしたファイルを file_put_contents で上書き保存する
  4. 上書きしたものを fgetcsv で読込み展開する

めんどくさいですがしょうがないですね。

実際に書いたコード

#function内
    $filePath = "hoge/hoga/item_list_20200801_day_fuga.csv";

    # file -i ファイル名のコマンドで文字コードを調べる
    $command = "file -i " . $filePath ;
    $output = [];
    $status = "";
    exec($command, $output, $status);

    #エンコードを抜き取る
    preg_match("/charset=(.*)/", $output[0], $charset);

    #utf-8出ない場合処理実行
    if (!in_array("utf-8", $charset)) {
        #文字コードの変更※読込む前に変換しておく
        $sjis = file_get_contents(filePath);
        $utf8 = mb_convert_encoding($sjis, 'UTF-8', 'Shift_jis');
        file_put_contents($data[0], $utf8);
    }

    $row = 0;
    // ファイル存在チェック
    if (($handle = fopen(filePath , "r")) !== FALSE) {
        // 1行ずつ読込
        $value = [];
        while (($value = fgetcsv($handle))) {
            //レコード開始まで無視
            if(($row >=6)&&(!empty($value[0]))){
                //商品URLをdumpする
                var_dump(strval($value[5]));
            }
            $row++;
        }
        fclose($handle);
    }

まとめ

自分なりに「結構かけるようになってきたな」などと思っていた矢先にコレです。

まだまだ足りない知識は多いなぁとシミジミ感じました。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください