1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
<?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;
}
}
?>