stream_get_contentsで空文字・取得できないとき

  • このエントリーをはてなブックマークに追加
  • Pocket

こんにちは。タクマ™ [@suwaru_blog] です。

PHP の stream_get_contents() で CSV ファイルの中身を取り出そうとしても空文字…
で先日ハマったので共有します。

結論は「ストリームのモードをちゃんと見ろ」でした。

問題のソースコード

適当ですが、以下のソースコードがあったとします。
サーバ上で CSV ファイル生成して Windows 向けに文字コードも設定するって感じです。

$f = fopen($filepath, 'w');

if ($f) {
    // CSV のヘッダー作成
    $header = array(
        "ユーザID",
        "登録日時"
    );

    fputcsv($f, $header);

    foreach ($data as $d) {
        // CSV のボディ作成
        $result = array(
            $d->user_id,
            date('Y/n/j G:i', strtotime($d->created_at))
        );
        fputcsv($f, $result);
    }

    // バッファ先頭に戻ってコンテンツ取得する
    rewind($f);

    // ...つもりがなぜかここで空文字しか取得できない!
    $buffer = str_replace("\n", "\r\n", stream_get_contents($f));

    // 文字コード SJIS でエンコード
    $sjis = mb_convert_encoding($buffer, 'sjis-win', 'UTF-8');
    fclose($f);

    $f = fopen($filepath, 'w');

    // エンコードデータで上書き
    fwrite($f, $sjis);
    fclose($f);

    // 空っぽの CSV ファイルができあがる...\(^o^)/

}

これだと空っぽの CSV ファイルができあがり。

$buffer = str_replace("\n", "\r\n", stream_get_contents($f));
上記の stream_get_contents($f) で空文字が返っていることが判明しました。

原因はストリームの mode

その原因は $f = fopen($filepath, 'w'); にありました。
mode 'w' ではファイルストリームの中身を取り出すことができません。

$f = fopen($filepath, 'w+b'); にすると取り出せました。

fopen() のドキュメントを読んでみた

ちゃんと PHP 公式ドキュメントに書かれていました。
モード w は「書き出しのみ」。

ファイル中身を置換したり、文字コードを設定するときには「読み込み」も必要。
モード w ではダメなのですね。

w

書き出しのみでオープンします。ファイルポインタをファイルの先頭に置き、 ファイルサイズをゼロにします。ファイルが存在しない場合には、 作成を試みます。

w+

読み込み/書き出し用でオープンします。 ファイルポインタをファイルの先頭に置き、 ファイルサイズをゼロにします。 ファイルが存在しない場合には、作成を試みます。

b

Windows上では、\nを\r\nに透過的に変換する text-mode変換フラグ(‘t’)が提供されます。 それに対し、’b’を使って強制的にバイナリモードにすることもできます。 その場合データの変換はされません。 このフラグを使用するには、’b’ または ‘t’を mode引数の最後に追加してください。

デフォルトの変換モードは SAPI と使用している PHP のバージョンによって異なります。 したがって、互換性の意味から、常に適切なフラグを指定することが推奨されます。 plain-text ファイルを使用する場合には ‘t’ モードを指定すべきであり、 改行に \n を使用すると、 メモ帳のようなアプリケーションで読めることを期待できます。 それ以外のケースでは ‘b’ を使うべきです。

バイナリファイルを扱っている際に ‘b’ フラグを指定しなかった場合、 画像ファイルが壊れたり、\r\n キャラクタがおかしくなる等の問題を抱えてしまうでしょう。

まとめ

単なる書き込みモードのストリームでstream_get_contents() を使った場合、
エラーが起きるわけでもなく、空文字が返ってくる。
その場合はストリームのモードを確認すること。

お仕事ください!

僕が代表を務める 株式会社 EeeeG では Web 制作・システム開発・マーケティング相談を行っています。
なにかお困りごとがあれば、Twitter DM や Web サイトからお気軽にご相談ください。

コメントを残す

*