SplFileObject + LimitIterator + offset

4

我有一个数据文件,其中有两行(这里只是为了举例,实际上该文件可能包含数百万行),我使用SplFileObject和LimitIterator进行偏移。但是在某些情况下,这种组合的行为很奇怪:

$offset = 0;
$file = new \SplFileObject($filePath);
$fileIterator = new \LimitIterator($file, $offset, 100);
foreach ($fileIterator as $key => $line) {
  echo $key;
}

输出结果为:01

但是当$offset设置为1时,输出为空(foreach不会迭代任何行)。

我的数据文件包含以下内容:

{"generatedAt":1434665322,"numRecords":"1}
{"id":"215255","code":"NB000110"}

我做错了什么吗?
谢谢

我不知道这是否重要,但是你的数据文件第一行末尾缺少一个引号(")。 - Blackus
这只是我在格式化时犯的一个小错误。 - Miroslav Hruška
所以,这可以被标记为 PHP 的 bug 吗?或至少是令人困惑的行为?我理解得对吗? - Miroslav Hruška
我认为这确实是“令人困惑的”。我没有预料到那种行为。我也认为它会搞乱foreach循环处理。我认为这是一个“文件结束”处理的边缘情况,没有被正确处理。这个问题出在SplFileObject上。 - Ryan Vincent
2个回答

1

必须:

使用 SplFileObject 处理从以下内容中的多条记录:

  • 给定开始记录编号
  • 给定记录数量或直到 EOF 为止。

问题在于,关于文件中的 最后一条记录SplFileObject 会感到困惑。这会阻止它在 foreach 循环中正常工作。

此代码使用 SplFileObject 和“跳过记录”、“处理记录”。不幸的是,它无法使用 foreach 循环。

  • 从文件开头跳过一个给定数量的记录($offset)。
  • 处理给定数量的记录或直到文件结束($recordsToProccess)。

代码:

<?php

$filePath = __DIR__ . '/Q30932555.txt';
// $filePath = __DIR__ . '/Q30932555_1.txt';

$offset = 1;
$recordsToProcess = 100;

$file = new \SplFileObject($filePath);

// skip the records
$file->seek($offset);

$recordsProcessed = 0;
while (     ($file->valid() || strlen($file->current()) > 0)
         &&  $recordsProcessed < $recordsToProcess
       ) {
    $recordsProcessed++;
    echo '<br />', 'current: ', $file->key(), ' ', $file->current();
    $file->next();
}

它有点“笨拙”,但似乎能够完成工作。 - Ryan Vincent
当然,这解决了我的问题。但我真的想使用LimitIterator,因为它有更易读的代码 :( - Miroslav Hruška
@MiroslavHruška,我试过了!可惜,当使用“LimitIterator”时,我无法得到任何有意义的东西。 - Ryan Vincent
我知道,谢谢Ryan。我会将此报告为PHP错误,然后我们再看看。 - Miroslav Hruška
@MiroslavHruška,我尝试使用“装饰器”来解决问题-这是一个很难解决的问题。我提供的代码可以工作,但是有些笨拙。我无法弄清楚如何在发生什么情况下使用foreach循环。我不喜欢我写的代码。 :-/ 它可以工作,但是有些“假”的感觉。;-/ - Ryan Vincent
问题在于当SplFileObject->valid()到达列表中的最后一条记录时,它返回false。它应该在移动到最后一条记录之后的下一条记录时返回false。因此,尽管“valid”报告为假,但仍需要对“current”记录进行长度测试;-/ - Ryan Vincent

0

阅读相关的 PHP bug 65601 建议添加 READ_AHEAD 标志来解决此问题。测试后发现其按照您的预期工作。

$offset = 0;
$file = new \SplFileObject($filePath);
$file->setFlags(SplFileObject::READ_AHEAD);
$fileIterator = new \LimitIterator($file, $offset, 100);
foreach ($fileIterator as $key => $line) {
  echo $key;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接