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