PHP5でfgetcsvが正常に動作しない

Posted on 7月 19, 2006
Filed Under PHP |

CSVのインポート機能を持ったシステムをPHP4環境からPHP5環境へ移行したら、
なぜかCSVデータを正しく読み込んでくれない。っていうか一文字目が文字化け。
超悩んだあげくぐーぐるさんで検索しても以下のような記事しかみつからず。

[PHP-dev 1205] PHP5のfgetcsv()関数について

人力検索はてな - PHP4からPHP5へソースの移(長いので略)

csvファイルを読み込むと1バイト目の日本語が文字化け

3つ目の掲示板のyossyはあたくし自身なんですが・・・。

setlocaleとかいろいろ試してもしても結局読み込まれるCSVの文字コードは
ほとんどSJISなせいなためかなんだかうまくいきません。

ちなみに検証環境はほぼFedoraCore4のデフォルトです。
PHPは5.0.4だったような気がします。

で、結局回避策として正規表現でCSVをぶった切ることにしました。

3つめのリンク先の掲示板に正規表現でCSV読み込む関数を書いたんですが、
改行を含んだCSVファイルには対応していなかったので
改行にも対応した関数を作りました。

Excel形式(というかRFC4180)に準拠したデータなら大丈夫・・・かな?

既存のfgetcsvとほとんど同じような感覚で使えるはず。うん。

<?php
    /**
     * ファイルポインタから行を取得し、CSVフィールドを処理する
     * @param resource handle
     * @param int length
     * @param string delimiter
     * @param string enclosure
     * @return ファイルの終端に達した場合を含み、エラー時にFALSEを返します。
     */
    function fgetcsv_reg (&$handle, $length = null, $d = ',', $e = '"') {
        $d = preg_quote($d);
        $e = preg_quote($e);
        $_line = "";
        while ($eof != true) {
            $_line .= (empty($length) ? fgets($handle) : fgets($handle, $length));
            $itemcnt = preg_match_all('/'.$e.'/', $_line, $dummy);
            if ($itemcnt % 2 == 0) $eof = true;
        }
        $_csv_line = preg_replace('/(?:\r\n|[\r\n])?$/', $d, trim($_line));
        $_csv_pattern = '/('.$e.'[^'.$e.']*(?:'.$e.$e.'[^'.$e.']*)*'.$e.'|[^'.$d.']*)'.$d.'/';
        preg_match_all($_csv_pattern, $_csv_line, $_csv_matches);
        $_csv_data = $_csv_matches[1];
        for($_csv_i=0;$_csv_i<count($_csv_data);$_csv_i++){
            $_csv_data[$_csv_i]=preg_replace('/^'.$e.'(.*)'.$e.'$/s','$1',$_csv_data[$_csv_i]);
            $_csv_data[$_csv_i]=str_replace($e.$e, $e, $_csv_data[$_csv_i]);
        }
        return empty($_line) ? false : $_csv_data;
    }
?>

この関数を↓こんな感じで。

<?php
    $row = 1;
    $handle = fopen("test.csv", "r");
    while (($data = fgetcsv_reg($handle)) !== false) {
        $_enc_to=mb_internal_encoding();
        $_enc_from=mb_detect_order();
        mb_convert_variables($_enc_to,$_enc_from,$data);
        $num = count($data);
        echo "<p> $num fields in line $row: </p><br />n";
        $row++;
        for ($c=0; $c < $num; $c++) {
            echo nl2br($data[$c]) . "<br />n";
        }
    }
    fclose($handle);
?>

Comments

Leave a Comment

If you would like to make a comment, please fill out the form below.

Name (必須)

Email (必須)

ウェブサイト

コメント

Protected by WP-Hashcash.

12 Comments so far
  1. Jun Kuwamura 12月 14, 2008 22:10:01

    ありがとうございます。
    pukiwikiのトラックバックのバグ修正に使わせてもらいました。

  2. n-brid 12月 17, 2008 4:26:04

    すばらしいです。
    おかげさまでシンプルに処理できるようになりました。ありがとうございます。

  3. 池田@ママ 12月 24, 2008 20:06:59

    ありがとうーーーー
    感謝します!

  4. takus 1月 20, 2009 22:31:03

    素晴らしいです。
    一発で問題が解決しました。
    ありがとうございます。

  5. hirrroo 1月 21, 2009 14:09:58

    悩み解決しました。
    ありがとうございます!!

  6. Restromic 1月 27, 2009 3:36:57

    感謝!!!

  7. もぎゃ 4月 21, 2009 21:59:17

    この関数、間違って「”」が奇数個あるファイルを読み込むと、whileループから抜けられなくなりませんか?

    関数の5行目
    $_line .= (empty($length) ? fgets($handle) : fgets($handle, $length))
    を、こんなふうにすれば直るような気がします。

    $s = (empty($length) ? fgets($handle) : fgets($handle, $length));
    if (!$s) {break;}

  8. SE風味 6月 7, 2009 10:48:22

    fgetcsv_reg にはお世話になりました。ありがとうございます。
    で、上のコメントの無限ループについて、
    while ($eof != true) {

    while (($eof != true)and(!feof($handle))) {
    に書きかえればOKではないかと。

  9. IIS 6月 15, 2009 9:20:07

    助かりました。ありがとうございます

  10. nzk 7月 19, 2009 11:00:51

    素晴らしすぎます!ありがとうございます!

  11. Tadaske Watanabe 10月 30, 2009 16:47:14

    同じ問題で困っていたところでした。
    ありがとうございます!

    本当に助かりました。:-)

  12. hideshi 12月 26, 2009 18:00:02

    私もこれで困っていました。非常に助かりました。ありがとうございます。
    netcommonsで応用させて頂きました。

    1行用
    function fgetcsv_reg($line, $length = null, $d = ‘,’, $e = ‘”‘) {
    $d = preg_quote($d);
    $e = preg_quote($e);
    $itemcnt = preg_match_all(’/’.$e.’/', $line, $dummy);
    $_csv_line = preg_replace(’/(?:\r\n|[\r\n])?$/’, $d, trim($line));
    $_csv_pattern = ‘/(’.$e.’[^’.$e.’]*(?:’.$e.$e.’[^’.$e.’]*)*’.$e.’|[^’.$d.’]*)’.$d.’/';
    preg_match_all($_csv_pattern, $_csv_line, $_csv_matches);
    $_csv_data = $_csv_matches[1];
    for($_csv_i=0;$_csv_i

Recently


Categories


Archives


Wordpress Themes