質問するログイン新規登録

回答編集履歴

7

NoRewindIteratorは内側じゃないとダメっぽい

2017/05/27 06:32

投稿

mpyw
mpyw

スコア5223

answer CHANGED
@@ -16,13 +16,15 @@
16
16
  $file->setFlags(File::READ_CSV | File::DROP_NEW_LINE); // CSVモード (但し空行読み飛ばしモードは使えない)
17
17
  $file->setCsvControl("\t"); // セパレータをタブ文字に
18
18
 
19
+ // Rewindされないようにラップする
20
+ $it = new NoRewindIterator($file);
19
21
  // 空行を読み飛ばすようにラップする
20
- $it = new CallbackFilterIterator($file, function ($row) { return $row !== [null]; });
22
+ $it = new CallbackFilterIterator($it, function ($row) { return $row !== [null]; });
21
23
  // $pオフセットから$e件に制限するようにラップする
22
24
  $it = new LimitIterator($it, $p, $e);
23
25
 
24
- // rewindを防ぎながら配列に変換
26
+ // 配列に変換
25
- $records = iterator_to_array(new NoRewindIterator($it));
27
+ $records = iterator_to_array($it);
26
28
 
27
29
  ?>
28
30
  <!DOCTYPE html>

6

もうちょいシンプルに

2017/05/27 06:32

投稿

mpyw
mpyw

スコア5223

answer CHANGED
@@ -18,13 +18,11 @@
18
18
 
19
19
  // 空行を読み飛ばすようにラップする
20
20
  $it = new CallbackFilterIterator($file, function ($row) { return $row !== [null]; });
21
- // rewindさせないようにラップする
22
- $it = new NoRewindIterator($it);
23
21
  // $pオフセットから$e件に制限するようにラップする
24
22
  $it = new LimitIterator($it, $p, $e);
25
23
 
26
- // 配列に変換
24
+ // rewindを防ぎながら配列に変換
27
- $records = iterator_to_array($it);
25
+ $records = iterator_to_array(new NoRewindIterator($it));
28
26
 
29
27
  ?>
30
28
  <!DOCTYPE html>

5

バグ修正 \(File::READ_AHEADおよびFile::SKIP_EMPTYはHTTP URLに対しては使えない模様\)

2017/05/27 06:28

投稿

mpyw
mpyw

スコア5223

answer CHANGED
@@ -13,15 +13,19 @@
13
13
 
14
14
  // SplFileObjectでURLをオープンする
15
15
  $file = new File('http://example.com/ttt.txt', 'rb');
16
- $file->setFlags(File::READ_CSV | File::SKIP_EMPTY | File::READ_AHEAD | File::DROP_NEW_LINE); // CSVモード
16
+ $file->setFlags(File::READ_CSV | File::DROP_NEW_LINE); // CSVモード (但し空行読み飛ばしモードは使えない)
17
17
  $file->setCsvControl("\t"); // セパレータをタブ文字に
18
18
 
19
- // SplFileObjectはイテレータでもあるが,iterator_to_array適用時に
20
- // rewind処理が発生してしまうのでNoRewindIteratorでラップする。
21
- // 更に,必要な部分だけを効率的に残せるLimitIteratorでラップする。
22
- // 最後配列に変換する
19
+ // 空行を読み飛ばすようラップする
20
+ $it = new CallbackFilterIterator($file, function ($row) { return $row !== [null]; });
21
+ // rewindさせないようにラップする
22
+ $it = new NoRewindIterator($it);
23
+ // $pオフセットから$e件に制限するようにラップする
23
- $records = iterator_to_array(new LimitIterator(new NoRewindIterator($file), $p, $e));
24
+ $it = new LimitIterator($it, $p, $e);
24
25
 
26
+ // 配列に変換
27
+ $records = iterator_to_array($it);
28
+
25
29
  ?>
26
30
  <!DOCTYPE html>
27
31
 

4

蛇足

2017/05/27 06:25

投稿

mpyw
mpyw

スコア5223

answer CHANGED
@@ -55,4 +55,6 @@
55
55
  - [ob_start](http://php.net/manual/ja/function.ob-start.php)を使って出力HTMLのバッファリングを行っている
56
56
  - エラーや例外が発生したときには正常系で使うHTMLのバッファを破棄してエラー用のHTMLに切り替える処理を書いている
57
57
 
58
- 上記を満たす場合,一時的な配列を生成する必要すらありません。イテレータのままそのまま`foreach`で回してもらって構いません。条件を満たしていないのにやってしまうと,`foreach`ループ中に通信障害が発生したときにHTMLが中途半端な状態でぶっ壊れて出力されてしまうので注意
58
+ 上記を満たす場合,一時的な配列を生成する必要すらありません。イテレータのまま`foreach`で回してもらって構いません。ただし条件を満たしていないのにやってしまうと,`foreach`ループ中に通信障害が発生したときにHTMLが中途半端な状態でぶっ壊れて出力されてしまうので注意
59
+
60
+ 一度配列を作る意味としては,「一度作ってしまえば配列は絶対に壊れることが無い」と保証できるからですね。HTTP経由でURLをオープンしているイテレータは不安定なことを意識しなければなりません。

3

蛇足

2017/05/26 14:06

投稿

mpyw
mpyw

スコア5223

answer CHANGED
@@ -48,4 +48,11 @@
48
48
  <li><?=($i+1)."\t".implode("\t", array_map('htmlspecialchars', $record))?></li>
49
49
  <?php endforeach; ?>
50
50
  </ul>
51
- ```
51
+ ```
52
+
53
+ 蛇足ですが,
54
+
55
+ - [ob_start](http://php.net/manual/ja/function.ob-start.php)を使って出力HTMLのバッファリングを行っている
56
+ - エラーや例外が発生したときには正常系で使うHTMLのバッファを破棄してエラー用のHTMLに切り替える処理を書いている
57
+
58
+ 上記を満たす場合,一時的な配列を生成する必要すらありません。イテレータのままそのまま`foreach`で回してもらって構いません。(条件を満たしていないのにやってしまうと,`foreach`ループ中に通信障害が発生したときにHTMLが中途半端な状態でぶっ壊れて出力されてしまうので注意)

2

LimitIterator使ったほうがよさそう

2017/05/26 14:04

投稿

mpyw
mpyw

スコア5223

answer CHANGED
@@ -1,5 +1,7 @@
1
- [PHP: NoRewindIterator - Manual](http://php.net/manual/ja/class.norewinditerator.php) なんてあったんですね,勉強になりました。不可逆なストリームを扱う際には必須ですね。
1
+ [NoRewindIterator](http://php.net/manual/ja/class.norewinditerator.php) なんてあったんですね,勉強になりました。不可逆なストリームを扱う際には必須ですね。
2
2
 
3
+ あと調べていたら[LimitIterator](http://php.net/manual/ja/class.limititerator.php)なるものもあるようです!一度全部配列に変換してから切り取るよりも,部分的にイテレータの段階で読み捨てを行ったほうがメモリ効率が圧倒的にいいので是非こちらで。
4
+
3
5
  ```php
4
6
  <?php
5
7
 
@@ -14,9 +16,11 @@
14
16
  $file->setFlags(File::READ_CSV | File::SKIP_EMPTY | File::READ_AHEAD | File::DROP_NEW_LINE); // CSVモード
15
17
  $file->setCsvControl("\t"); // セパレータをタブ文字に
16
18
 
17
- // イテレータを配列変換して切り取り
19
+ // SplFileObjectはイテレータでもあるが,iterator_to_array適用時
18
- // 但し最初にrewind処理が発生してしまうのでNoRewindIteratorでラップする
20
+ // rewind処理が発生してしまうのでNoRewindIteratorでラップする
21
+ // 更に,必要な部分だけを効率的に残せるLimitIteratorでラップする。
22
+ // 最後に配列に変換する。
19
- $records = array_slice(iterator_to_array(new NoRewindIterator($file)), $p, $e, true);
23
+ $records = iterator_to_array(new LimitIterator(new NoRewindIterator($file), $p, $e));
20
24
 
21
25
  ?>
22
26
  <!DOCTYPE html>

1

シングルクオート派

2017/05/26 13:59

投稿

mpyw
mpyw

スコア5223

answer CHANGED
@@ -10,7 +10,7 @@
10
10
  $e = filter_input(INPUT_GET, 'e');
11
11
 
12
12
  // SplFileObjectでURLをオープンする
13
- $file = new File("http://example.com/ttt.txt", 'rb');
13
+ $file = new File('http://example.com/ttt.txt', 'rb');
14
14
  $file->setFlags(File::READ_CSV | File::SKIP_EMPTY | File::READ_AHEAD | File::DROP_NEW_LINE); // CSVモード
15
15
  $file->setCsvControl("\t"); // セパレータをタブ文字に
16
16