【PHP】ストリームで読み書きする時に文字コードを変換する

 PHP にはストリームという仕組みがあります。ざっくばらんに言えばこれは部分的にリソースを読み書きする仕組みです。メモリに優しく、また大量の動作を全て完了させずとも必要な部分だけ抜き出したりできます。
PHP: ストリーム – Manual
 PHP でストリームを使うタイミングで最も多いのは恐らく fopen 関数を使ったファイルやURLの読み書きでしょう。
PHP: fopen – Manual
 そして日本人で文字コードの変換を頻繁に行うのは SJIS と UTF-8 でしょう。Excel で読み書きする CSV ファイルのデフォルトがこれです。
 よくある mb_convert_encoding 関数を用いてストリームの SJIS で書かれた CSV を UTF-8 として読む処理は次の様になります。

    /**
     * CSVファイルを読んで二次元配列化して返す
     * @param string $file_path
     * @return array CSVファイルの中身の二次元配列
     */
    function get_csv_array($file_path)
    {
        $csv_body = [];
        // ファイルオープン
        if (($handle = fopen($file_path, 'rb')) !== false) {
            // ファイルの1行をCSVフィールドとして配列で読み込み
            while (($line = fgetcsv($handle)) !== false) {
                $csv_body[] = array_map(
                    static function ($cell) {
                        // 読み込んだ配列の一つ一つ(セル)を SJIS から UTF-8に変換
                        return mb_convert_encoding($cell, 'UTF-8', 'sjis-win');
                    },
                    $line
                );
            }
            // 読み込む行がなくなったのでクローズ
            fclose($handle);
        }
        
        return $csv_body;
    }

 動くは動きますし正確な動作なのですが、行と列の二重ループの中で都度関数を呼び出すのが気に食わないです(読み込み対象のCSVファイルがよっぽど巨大でなければ実害はないです)。そこでこの二重ループを考えなくとも文字コードを変換する方法を紹介します。
 ソースコードは以下です。

    /**
     * CSVファイルを読んで二次元配列化して返す
     * @param string $file_path
     * @return array CSVファイルの中身の二次元配列
     */
    function get_csv_array($file_path)
    {
        $csv_body = [];
        // ファイルオープン
        if (($handle = fopen($file_path, 'rb')) !== false) {
            // SJIS-winからUTF-8へ変換するフィルター
            // @see https://www.php.net/manual/ja/function.stream-filter-append.php PHP: stream_filter_append - Manual
            // @see https://www.php.net/manual/ja/function.iconv.php PHP: iconv - Manual
            stream_filter_append($handle, 'convert.iconv.CP932/UTF-8//TRANSLIT', STREAM_FILTER_READ);
            while (($line = fgetcsv($handle)) !== false) {
                $csv_body[] = $line;// 既に UTF-8 として読み込まれたセルの配列を返り値用配列に追加
            }
            fclose($handle);// 読み込む行がなくなったのでクローズ
        }
        return $csv_body;
    }

 PHP にはストリームに対して文字コードを変換するフィルターが存在し、これを用いることによって高速かつシンプルなコードで文字コード変換を行えます。
 使っているのはソースコードのコメント中にもある様に stream_filter_append という関数です。
PHP: stream_filter_append – Manual
 この関数ではストリームに流れるデータを加工するフィルターを追加できます。使えるフィルターは stream_get_filters で取得でき、stream_filter_register で自由に追加できます。
PHP: stream_get_filters – Manual
PHP: stream_filter_register – Manual
PHP: iconv – Manual
 ソースコードで用いている ‘convert.iconv.CP932/UTF-8//TRANSLIT’ はおそらく PHP 内の iconv 関数を PHP 内部で適用しています(PHP内部の関数のハッシュテーブルっぽいものにフィルターの有無を聞いてる感じ)。iconv 関数は文字コード変換関数です。これを / の前半を第一引数、後半を第二引数として呼び出している感じです。iconv 関数は名前的におそらく Linux の iconv コマンド相当で動きます。

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG