サンプル・プログラムの実行例

サンプル・プログラムのダウンロード
csv2table2.php | サンプル・プログラム本体。 |
sample2.csv | サンプルCSVファイル。 |
バージョン | 更新日 | 内容 |
---|---|---|
3.1.0 | 2023/04/29 | 囲い文字の中にデリミタがある場合の処理を追加 |
3.0 | 2021/05/29 | PHP8対応 |
2.1 | 2009/01/24 | 空白を に |
2.0 | 2008/07/26 | 初版 |
解説:空白文字に対応した fgetcsv

CSVファイルの読み込みは、関数 fgets を利用している。
読み取りに失敗したり、文字列長がゼロの場合(fgetsは改行文字まで読み込むので、空行でも文字列長がゼロになることはない)、ファイルの終端に達したものと考える。

次に、空白文字を残し、行末の改行文字のみを削除するために関数 rtrim を利用する。

CSVを個々のフィールドに分解するのに、関数 preg_split を利用した。
正規表現を用いてフィールドを分解することができる

次に、フィールド囲い文字を削除するために関数 preg_replace を利用した。
125: /**
126: * 空白文字に対応した fgetcsv関数.
127: * デリミタやフィールド囲い文字,エンコードを指定できる.
128: * @param object $handle ファイルポインタ
129: * @param int $length 1行の最大長(省略可)
130: * @param string $delimiter フィールドのデリミタ;正規表現指定 (省略時は ,)
131: * @param string $enclosure フィールド囲い文字;正規表現指定(省略時は ")
132: * @param string $encode ファイルのエンコード(省略時はauto)
133: * @return array CSVフィールド
134: */
135: function myfgetcsv($handle, $length=0, $delimiter='/,/ui', $enclosure='"', $encode='auto') {
136: //1行の最大長を考慮してファイル読み込みする.
137: $s = ($length > 0) ? fgets($handle, $length) : fgets($handle);
138: if ($s == FALSE || $s == '') return FALSE;
139: //エンコード変換
140: $s = mb_convert_encoding($s, INTERNAL_ENCODING, 'auto');
141: //1行の右側の空白を削除する.
142: $s = rtrim($s);
143: //カラムに分解する.
144: $arr1 = preg_split($delimiter, $s);
145: $arr2 = array();
146: $arr3 = array();
147:
148: //カラムを1個ずつ処理する
149: $offset = 0;
150: $flag = FALSE;
151: foreach ($arr1 as $key=>$val) {
152: $pat1 = '/^\\' . $enclosure . '([^\\' . $enclosure . ']+)\\' . $enclosure . '$/ui';
153: $pat2 = '/^\\' . $enclosure . '([^\\' . $enclosure . ']*)$/ui';
154: $pat3 = '/^([^\\' . $enclosure . ']*)\\' . $enclosure . '$/ui';
155: //囲い文字に囲まれている場合
156: if (preg_match($pat1, $val, $arr3) > 0) {
157: $arr2[$offset] = $arr3[1];
158: $offset++;
159: //囲い文字の中にデリミタがある場合(開始処理)
160: } else if (preg_match($pat2, $val, $arr3) > 0) {
161: $arr2[$offset] = $arr3[1];
162: $flag = TRUE;
163: } else if ($flag) {
164: //囲い文字の中にデリミタがある場合(終了処理)
165: if (preg_match($pat3, $val, $arr3) > 0) {
166: $arr2[$offset] .= $arr3[1];
167: $flag = FALSE;
168: $offset++;
169: } else {
170: $arr2[$offset] .= $val;
171: }
172: //囲まれていない場合
173: } else {
174: $arr2[$offset] = $val;
175: $offset++;
176: }
177: }
178: return $arr2;
179: }
解説:CSVを変換

htmlentities を用い、HTMLタグやPHPスクリプトとして解釈される可能性がある特殊文字をHTMLエンティティ(< >など)に変換してから表示する。
そのまま表示してしまうと、スクリプトとして実行され、システムの弱点を突かれる恐れがあるからである。
181: /**
182: * CSV形式ファイル名を指定し,TABLEタグ文字列を求める.
183: * エラーが発生したときには$errmsgに代入する.
184: * @param string $fname CSVファイル名
185: * @param string $errmsg エラーメッセージ格納用
186: * @return string TABLEタグ文字列
187: */
188: function csv2table($fname, &$errmsg) {
189: $html =<<< EOT
190: <table class="plists">
191:
192: EOT;
193:
194: //CSV形式ファイルを読み込みオープンする.
195: if (($infp = fopen($fname, 'r')) == FALSE) {
196: $errmsg = 'サーバ・トラブルが発生しました';
197: return FALSE;
198: }
199: //CSV形式ファイルを1行ずつ読み込む.
200: while (($csv = myfgetcsv($infp, MAX_LINE)) !== FALSE) {
201: $html .= "<tr>\n";
202: foreach ($csv as $key=>$val) {
203: //特殊文字をHTMLエンティティに変換する.
204: $val = htmlentities($val, ENT_QUOTES, INTERNAL_ENCODING);
205: //デリミタ間にデータが存在しない場合は空白文字にする.
206: if ($val == '') {
207: $val =' ';
208: }
209: //空白を に変換する.
210: $val = preg_replace('/ /u', ' ', $val);
211: //カラム1つ分のHTMLタグを追記する.
212: $html .= "<td>{$val}</td>\n";
213: }
214: //1行の最後
215: $html .= "</tr>\n";
216: }
217: //表の最後
218: $html .="</table>";
219:
220: fclose($infp);
221:
222: return $html;
223: }
しかし、関数fgetcsvは、CSVファイル中の空白文字を無視する。空白文字を含む場合は、フィールド囲い文字を利用し、"_空白文字がある" のようにするのがCSVの定石なので、このような動作になっているものと思われる。
そこで今回は、フィールド区切り文字が無くとも空白文字を扱うことができ、さらにデリミタやフィールド囲い文字を正規表現で指定できるオリジナルのfgetcsvをつくってみることにする。
(2023年4月29日)囲い文字の中にデリミタがある場合の処理を追加