エクセルで出力されたTSVを想定。
CSVのほうが知名度はあるが、TSVが好ましい(区切りには制御文字のほうが適している、それこそ制御文字の本分でしょう、という気がする)。
エクセルでデータを作る人、つまり非プログラマー、非SEの人は普通TSVのことを知らないが、そこは事前に説明してTSVで出力するようお願いしておく。
TSVに関する大まかな仕様
- 改行を含む列は"で始まり"で終わる
- タブ、二重引用符を含んでもいい
- SJIS(PHPプログラム内ではUTF8に変換して処理する)
TSVファイルを読み込んで連想配列の配列に変換(PHP)
/* * TSVファイルを読み込んで連想配列の配列に変換 * 引数:処理結果(参照渡し)、TSVファイルのパス */ function parseTSV(&$records, $path){ $fp = fopen($path, 'r'); if($fp===FALSE){ return FALSE; } $records = array(); $errors = array(); //$cnt = 0; // これはデバッグ専用のリミット(後で削除) // while(!feof($fp) && $cnt < 10){ while(!feof($fp)){ $line = fgets($fp); $line = mb_convert_encoding($line, 'UTF-8', 'SJIS-win'); $line = str_replace(array("\r\n","\r"), "\n", $line); $len = mb_strlen($line, 'UTF-8'); $state = 'START'; $flg = 0; $buf = ''; $fields = array(); // $cnt++; for($i=0; $i<$len; $i++){ $char = mb_substr($line, $i, 1, 'UTF-8'); if($state=='START'){ if($char === '"' && $flg == 0) { $flg = 1; $state = 'MID'; } elseif($char === "\n" && $flg == 0) { break; } elseif($char === "\t") { $state = 'END'; } else { $buf .= $char; $state = 'MID'; } } elseif($state == 'MID') { if($char === "\t") { if($flg){ $buflen = mb_strlen($buf, 'UTF-8'); if($buflen && mb_substr($buf, $buflen - 1, 1, 'UTF-8') == '"') { $state = 'END'; } else { $char = ' '; } } else { $state = 'END'; } } elseif($char === "\n"){ if($flg){ $buf .= $char; // データが次の行に続いている場合 $line = fgets($fp); $line = mb_convert_encoding($line, 'UTF-8', 'SJIS-win'); $line = str_replace(array("\r\n","\r"), "\n", $line); $len = mb_strlen($line, 'UTF-8'); $i = -1; // ここは強引 } else { $state = 'END'; } } else { $buf .= $char; } } if($state == 'END') { if($flg){ $buf = mb_substr($buf, 0, mb_strlen($buf, 'UTF-8') - 1, 'UTF-8'); } $buf = mb_ereg_replace('""', '"', $buf); // 連続したダブルクォートは1つに // 連想配列に追加して、初期化 $fields[] = $buf; $state = 'START'; $flg = 0; $buf = ''; } } // 1レコード読み込み終了 if(count($fields) > 0) { $records[] = $fields; } } fclose($fp); return TRUE; }
0 件のコメント:
コメントを投稿