omoonのブログ

旅の思い出を写真とともに書いてみるか。

fputcsv の改行コードを CR+LF にするには

php5.1 から使える fputcsv という関数はとても便利。

例えば、こんな感じで、

<?php
$data = array(
    array(1, '松田', '聖子', '2'),
    array(2, '川本', '真琴', '2'),
    array(3, 'つのだ ★', 'ひろ', '1'),
);
$fp = fopen("php://output", "w");
foreach ($this->data as $line) {
    fputcsv($fp, $line);
}
?>

とやると

1,松田,聖子,2
2,川本,真琴,2
3,"つのだ ★",ひろ,1

こんなふうになる。空白や改行などが入っていてもええ感じにやってくれる。詳しくはここ参照。

ただ、改行コードが OS に依存するので、サーバが Linux でクライアントが Windows とかだとちょっと困ったりする。
具体的には、お客さんから電話で、ダウンロードした csvWindows のメモ帳で開いたら改行されてないんですぅ〜! ぷんぷくり〜ん(怒)って言われたるするってこと。

つまり、Linux だと改行コードが LF なので、改行を (CR+LF) で扱う Windows 様のメモ帳ちゃんではうまく改行されない、と。

なので、解決策を考えた。使ったのは

あたり。

簡単に言うと、ストリームの出力時にフィルタを登録できるので、行末の改行を強制的にCR+LFに変換するフィルタを通したらいいのでは、ってこと。

ついでに Windows なので、mb_convert_variable を使って、文字コードも cp932 に変換してます。

詳しくはコード見てください。

<?php
class csv
{

    function setData($data) {
        $this->data = $data;
    }

    function getCsv($is_ms = false) {
        $fp = fopen("php://output", "w");
        if (true === $is_ms) {
            stream_filter_register("msLineEnding", "ms_line_ending_filter");
            stream_filter_append($fp, "msLineEnding");
        }
        foreach ($this->data as $line) {
            if (true === $is_ms) {
                mb_convert_variable('cp932', 'utf-8', $line);
            }
            fputcsv($fp, $line);
        }
        fclose($fp);
    }

    function getCsvMs() {
        $this->getCsv(true);
    }
}

class ms_line_ending_filter extends php_user_filter
{
    function filter($in, $out, &$consumed, $closing) {
        while ($bucket = stream_bucket_make_writeable($in)) {
            $bucket->data = preg_replace("/\n$/", "", $bucket->data);
            $bucket->data = preg_replace("/\r$/", "", $bucket->data);
            $bucket->data = $bucket->data . "\r\n";
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}
?>

utf-8 + LF の時は

<?php
$data = array(
    array(1, '松田', '聖子', '2'),
    array(2, '川本', '真琴', '2'),
    array(3, 'つのだ ★', 'ひろ', '1'),
);
$csv = new csv();
$csv->setData($data);
$csv->getCsv();
?>

cp932 + CRLF の時は

<?php
$data = array(
    array(1, '松田', '聖子', '2'),
    array(2, '川本', '真琴', '2'),
    array(3, 'つのだ ★', 'ひろ', '1'),
);
$csv = new csv();
$csv->setData($data);
$csv->getCsvMs();
?>

では、また。