<?php /** * fgetscvの代わりに使うためのクラス。 * @author mocho * @package jp.aimslib2.io */ class CsvReader { /** * 読み込む対象のcsvファイル * @var String $file_path */ public $file_path; /** * 結果の配列 * @var array */ public $result; /** * 囲い文字 */ public $quote; /** * 区切り文字 */ public $delim; /** * 処理用の一時データ * @var String */ public $cell_data; /** * コンストラクタ * @param String $file_path 読み込むファイル名 */ public function __construct($file_path, $delim = "\t", $quote = "\"") { // ファイル名必須 $this->file_path = $file_path; // 初期設定 $this->quote = $quote; $this->delim = $delim; } /** * 読み込んで解析します。 * @return boolean 成功時、true */ public function parse($delim = null, $quote = null) { if (!file_exists($this->file_path)) { return false; } if (!is_readable($this->file_path)) { return false; } // 区切り文字の変更があれば変更 if ($delim != null) { $this->delim = $delim; } // 囲い文字の変更があれば変更 if ($quote != null) { $this->quote = $quote; } // 結果配列初期化 $this->result = array(); $fp = fopen($this->file_path, "r"); // 前のセルのデータ $buffer = ""; // 新しいセルか? $is_new_cell = true; // 行毎の一時データ $row = null; // 行毎に読み込み while (!feof($fp)) { $line = fgets($fp); $line = rtrim($line); $tmp_list = explode($this->delim, $line); // 全行からの続きであるか、新行であるかで違う if ($is_new_cell) { // CSV1行に該当 $row = array(); $buffer = ""; } // 各要素をチェック for ($x = 0; $x < count($tmp_list); $x++) { if ($is_new_cell) { $tmp = $tmp_list[$x]; } else { if ($x == 0) { // 1行目なら、区切りでなく改行 $tmp = $buffer . "\n" . $tmp_list[$x]; } else { $tmp = $buffer . $this->delim . $tmp_list[$x]; } } // 囲い文字で始まっていなければ、そのまま生データ if (substr($tmp, 0, 1) == $this->quote) { // 囲われている if (CsvReader::_privateEndsWithQuote($tmp)) { // 最後がちゃんと囲い文字で終わっていれば $tmp2 = CsvReader::_privateEscapeQuote($tmp); array_push($row, rtrim($tmp2)); if (!$is_new_cell) { // 前追加してなかったら、追加処理 $is_new_cell = true; } } else { // 囲われていない $buffer = $tmp; // 継続データ $is_new_cell = false; } } else { // 生データ array_push($row, rtrim($tmp)); } } if ($is_new_cell) { // 前追加してなかったら、追加処理 array_push($this->result, $row); } } fclose($fp); return true; } /** * 囲い文字で終わるか? * @param String $str 評価対象文字列 * @return boolean 囲い文字で終わる場合、true */ public function _privateEndsWithQuote($str) { //ErrorLogger::doOutput("_privateEndsWithQuote:" . $str, 0); if (substr($str, -1, 1) != $this->quote) { return false; } // 最後の文字は囲い文字、あとは、それがエスケープされているか? $result = true; $x = -2; while ($x > 0 - strlen($str)) { if (substr($str, $x, 1) != $this->quote) { return $result; } if ($result) { $result = false; } else { $result = true; } $x--; } // 囲われてない、というか、内容が無い return false; } /** * 余分な囲い文字を取り除きます。 * また、2重の""を元に戻します。 * @param String $str 文字列 * @return String 囲い文字が無い文字列 */ public function _privateEscapeQuote($str) { $str = trim($str, $this->quote); $str = str_replace($this->quote . $this->quote, $this->quote, $str); return $str; } /** * 結果を返します。 * 最後の1行が余計な場合、それを取り除きます。 */ public function getResult() { if (count($this->result) > 0) { $last = $this->result[count($this->result) - 1]; if (is_array($last) && count($last) == 1 && $last[0] === "") { // 空の最終行 array_pop($this->result); } } return $this->result; } } ?>