kazpgmの日記

『プログラム自動作成@自動生成』作成の日記

TOOL更新_ToDoリスト51.ファイルUPLOAD、CSV出力のみのPGM作成

ToDoリストhttp://d.hatena.ne.jp/kazpgm/2009040351.ファイルUPLOAD、CSV出力のみのPGM作成をつくりはじめている。CSV出力のみのPGMは作らないことにした。(すでにWEB画面上にCSV出力ボタンがあるのでいらないだろうということ。)
ファイルUPLOADのための基本CSV解析に何を使うかを調査した。(fgetcsvは使いたくないので。)

1.結果として「fgetExcelCSV」を使うことにした。
http://ter.ath.cx/yusei/yuseiki/PHP+fgetcsv%28%29%E3%81%AF%E4%BD%BF%E3%81%86%E3%81%AAで「NYSL Version 0.9982」ライセンス。

2.ソースに日本語コメントを書いてロジックを追った。短くきれいなロジックだと思う。実は追ってみるまでよくわからなかった。 (DOS窓でトレースして確認した。)

<?php
    // 例)
    //    ・入力データtest.csvの内容([]はわかりやすくするためで実データにはありません。)
    //      [a,b""b,"c,c","d""dd"]
    //      [aaa,b""bb,"c,cc","d""dd",,"eee"]
    //      [サンプルテスト,シンプルテスト,トンプルテスト,aサンプルテスト]
    //      []
    //      [ ]

    //    ・実行結果内容(DOS画面でテストした)
    //      C:\xampp\php>php -q test_ueki.php
    //      4 fields in line 1:
    //      [a]
    //      [b""b]
    //      [c,c]
    //      [d"dd]
    //      6 fields in line 2:
    //      [aaa]
    //      [b""bb]
    //      [c,cc]
    //      [d"dd]
    //      []
    //      [eee]
    //      4 fields in line 3:
    //      [サンプルテスト]
    //      [シンプルテスト]
    //      [トンプルテスト]
    //      [aサンプルテスト]
    //      1 fields in line 4:
    //      []
    //      1 fields in line 5:
    //      [ ]

    $row = 1;
    $handle = fopen("test.csv", "r");
    if (!$handle) {
        echo "not open data \n";
        exit;
    }
    mb_internal_encoding("SJIS-WIN"); // DOS窓でテストするで"SJIS-WIN"を入れた。
    $enc_to = mb_internal_encoding();

    while( $data = fgetExcelCSV_20090714($handle) ) {
        $num = count($data);
        echo "$num fields in line $row: \n";
        // データをエンコードする
        mb_convert_variables($enc_to, "SJIS-WIN", $data); 
        for ($i=0; $i < $num; $i++) {
            echo '[' . trim($data[$i]) . "]\n";
        }
        $row++;
    }
    fclose($handle);


    /**
     * ファイル1行を取得しCSV解析を行う。結果は配列で返却する
     * 2009/07/14 例とコメント文を入れてロジックを追ってみた。<br />
     *       ロジックは変更していません。コメントを取り除いたものは本家から取ってください。
     * @param resource handle
     * @param int length <= PHP 4.3.0からfgetsのlength指定なしの場合行末まで読み込むので使っていない。
     * @param string delimiter
     * @param string enclosure
     * @return ファイルの終端に達した場合を含み、エラー時にFALSEを返します。
     */
function fgetExcelCSV_20090714(&$fp , $length = null, $delimiter = ',' , $enclosure = '"') {
    $line = fgets($fp);
    if($line === false) { // 読み込みレコードがない場合
        return false;
    }
    $bytes = preg_split('//' , trim($line)); // 1行をトリムした後「1文字配列」にする
    // 例)
    //   ・$lineの内容:読み込みレコード
    //     a,b""b,"c,c","d""dd"
    //   ・$bytesの内容:「1文字配列」
    //     Array
    //     (
    //         [0] =>
    //         [1] => a
    //         [2] => ,
    //         [3] => b
    //         [4] => "
    //         [5] => "
    //         [6] => b
    //         [7] => ,
    //         [8] => "
    //         [9] => c
    //         [10] => ,
    //         [11] => c
    //         [12] => "
    //         [13] => ,
    //         [14] => "
    //         [15] => d
    //         [16] => "
    //         [17] => "
    //         [18] => d
    //         [19] => d
    //         [20] => "
    //         [21] =>
    //     )

    // 先頭と最後に不要項目(例でいえば[0]と[21])が前後に1つずつ出来るので削除する。
    array_shift($bytes);array_pop($bytes); 
    // 例)
    //   ・$bytesの内容:「1文字配列」
    //     Array
    //     (
    //         [0] => a
    //         [1] => ,
    //         [2] => b
    //         [3] => "
    //         [4] => "
    //         [5] => b
    //         [6] => ,
    //         [7] => "
    //         [8] => c
    //         [9] => ,
    //         [10] => c
    //         [11] => "
    //         [12] => ,
    //         [13] => "
    //         [14] => d
    //         [15] => "
    //         [16] => "
    //         [17] => d
    //         [18] => d
    //         [19] => "
    //     )

    $cols = array();                        // 「カラム配列」初期化。返却用ワーク
    $col = '';                              // 「1カラム作成用Wk」初期化。1カラム分を編集するワーク
    $isInQuote = false;                     // 「クオート出現フラグ」をOFFにする
    while($bytes) {                         // 「1文字配列」があるうちは繰り返す
        $byte = array_shift($bytes);        // 先頭1文字切り取って「現文字」とする。(配列は1つ小さくなる)
        if($isInQuote) {                    // 「クオート出現フラグ」ONの場合
            if($byte == $enclosure) {       // 「現文字」が「クオート」の場合
                if($bytes[0] == $enclosure) { // 「次文字」が「クオート」の場合
                                            //      例)
                                            //        「,"d""dd"」の間にある"が「現文字」、次の"が「次文字」
                                            //        ここで""を"に変換する。
                    $col .= $byte;          // 「現文字」(クオート)を「1カラム作成用Wk」に追加する
                    array_shift($bytes);    // 「次文字」を配列から削除する。
                } else {
                    $isInQuote = false;     // 「クオート出現フラグ」をOFFにする
                                            // かつ、値は読み飛ばす。(最後のクオートをイメージしている。)
                                            //   例)
                                            //     「,"c,c"」の最後の"、「,"d""dd"」の最後の"
                }
            } else {                        // 「現文字」が「クオート」以外の場合
                $col .= $byte;              // 「現文字」を「1カラム作成用Wk」に追加する
                                            //   例)
                                            //     「,"c,c","d""dd"」のc,cとddd
            }
        } else {                            // 「クオート出現フラグ」がOFFの場合
            if($byte == $delimiter) {       // 「現文字」が「デリミッタ」の場合
                                            //    例)
                                            //     「a,b""b,"c,c","d""dd"」のa,、b,、c",の,のこと
                                            //     ここまでで1カラム分「1カラム作成用Wk」に作成済み
                $cols[] = $col;             // 「1カラム作成用Wk」を「カラム配列」にセットする。1カラム分を編集完了
                $col = '';                  // 「1カラム作成用Wk」初期化
            // 「現文字」が「クオート」の場合かつ、「1カラム作成用Wk」初期状態の場合。(先頭のクオートを対象としている。)
            } elseif($byte == $enclosure && $col == '') { 
                $isInQuote = true;          // 「クオート出現フラグ」をONにする
                                            // かつ、値は読み飛ばす。
                                            //   例)
                                            //     「,"c,c"」の最初の"、「,"d""dd"」の最初の"
            } else {
                $col .= $byte;              // 「現文字」を「1カラム作成用Wk」に追加する
                                            //   例)
                                            //     「a,b""b」のaとb""b
            }
        }

        // 現在のレコードを処理し終わったのに、最後のクオートが出てきてない場合、次のレコードを読みにいく。
        while(!$bytes && $isInQuote) {      // 「1文字配列」がない。かつ「クオート出現フラグ」ONの場合
            $col .= "\n";                   // 「1カラム作成用Wk」に改行コード追加
            $line = fgets($fp);             // 次のレコードを読み込む
            if($line === false) {           // 読み込みレコードがない場合
                $isInQuote = false;         // 「クオート出現フラグ」をOFFにする
            } else {
                $bytes = preg_split('//' , trim($line)); // 1行を「1文字配列」にする
                // 先頭と最後に必要ない配列が1つできるので削除する。
                array_shift($bytes);array_pop($bytes);
            }
        }
    }
    $cols[] = $col;                         // 「カラム配列」に「1カラム作成用Wk」を追加する
    // 例)
    //   ・$colsの内容:「カラム配列」
    //    Array
    //    (
    //        [0] => a
    //        [1] => b""b
    //        [2] => c,c
    //        [3] => d"dd
    //    )

    return $cols;
}

?>


21:00-05:30