在“r”模式下使用 PHP 的 SplFileObject 时,$file->eof() 始终返回 false
-
13-12-2019 - |
题
为什么我的 PHP 脚本挂起?
$path = tempnam(sys_get_temp_dir(), '').'.txt';
$fileInfo = new \SplFileInfo($path);
$fileObject = $fileInfo->openFile('a');
$fileObject->fwrite("test line\n");
$fileObject2 = $fileInfo->openFile('r');
var_dump(file_exists($path)); // bool(true)
var_dump(file_get_contents($path)); // string(10) "test line
// "
var_dump(iterator_count($fileObject2)); // Hangs on this
如果我删除最后一行(iterator_count(...
)并将其替换为:
$i = 0;
$fileObject2->rewind();
while (!$fileObject2->eof()) {
var_dump($fileObject2->eof());
var_dump($i++);
$fileObject2->next();
}
// Output:
// bool(false)
// int(0)
// bool(false)
// int(1)
// bool(false)
// int(2)
// bool(false)
// int(3)
// bool(false)
// int(4)
// ...
这 $fileObject->eof()
总是返回 false 所以我得到一个无限循环。
为什么会发生这些事情?我需要得到行数。
解决方案
为什么会发生这些事情?
你正在体验一种特殊的方式 SplFileObject
类已写。无需打电话 next()
和 current()
方法——使用默认的(0
) 标志——迭代器永远不会向前移动。
这 iterator_count()
函数从不调用 current()
;它检查 valid()
并打电话 next()
仅有的。您的定制循环仅调用其中一个或另一个 current()
和 next()
.
这应该被认为是一个错误(无论是 PHP 本身,还是文档中的失败)和以下代码 应该 (并且没有)按预期工作。我邀请你 举报此不当行为.
// NOTE: This currently runs in an infinite loop!
$file = new SplFileObject(__FILE__);
var_dump(iterator_count($file));
解决方法
推动事情进展的一个快速回避方法是设置 READ_AHEAD
对象上的标志。这将导致 next()
方法读取下一个可用行。
$file->setFlags(SplFileObject::READ_AHEAD);
如果出于任何原因您不希望 预读 那么你必须调用两者 next()
和 current()
你自己。
回到最初的两个SplFileObject的问题
现在,以下内容应该按您的预期工作,允许附加到文件并读取其行数。
<?php
$info = new SplFileInfo(__FILE__);
$write = $info->openFile('a');
$write->fwrite("// new line\n");
$read = $info->openFile('r');
$read->setFlags(SplFileObject::READ_AHEAD);
var_dump(iterator_count($read));
其他提示
编辑01
如果您需要的是文件中的行数:
<?php
$path = tempnam(sys_get_temp_dir(), '').'.txt';
$fileInfo = new SplFileInfo($path);
$fileObject = $fileInfo->openFile('a+');
$fileObject->fwrite("Foo".PHP_EOL);
$fileObject->fwrite("Bar".PHP_EOL);
echo count(file($path)); // outputs 2
?>
.
编辑02
这是上面的代码,但由于文件指针而不输入Infinite循环:
<?php
$path = tempnam(sys_get_temp_dir(), '').'.txt';
$fileInfo = new SplFileInfo($path);
$fileObject = $fileInfo->openFile('a+');
$fileObject->fwrite("Foo".PHP_EOL);
$fileObject->fwrite("Bar");
foreach($fileObject as $line_num => $line) {
echo 'Line: '.$line_num.' "'.$line.'"'."<br/>";
}
echo 'Total Lines:' . $fileObject->key();
?>
.
输出
线:0“foo”
线:1“栏”
总线:2
原始答案
所应用的逻辑有点关。我简化了代码:
<?php
// set path to tmp with random file name
echo $path = tempnam(sys_get_temp_dir(), '').'.txt';
echo "<br/>";
// new object
$fileInfo = new \SplFileInfo($path);
// open to write
$fileObject = $fileInfo->openFile('a');
// write two lines
$fileObject->fwrite("Foo".PHP_EOL);
$fileObject->fwrite("Bar".PHP_EOL);
// open to read
$fileObject2 = $fileInfo->openFile('r');
// output contents
echo "File Exists: " .file_exists($path);
echo "<br/>";
echo "File Contents: " . file_get_contents($path);
echo "<br/>";
// foreach line get line number and line contents
foreach($fileObject2 as $line_num => $line) {
echo 'Line: '.$line_num;
echo ' With: "'.$line.'" is the end? '.($fileObject2->eof()?'yes':'no')."<br>";
}
?>
.
输出:
/tmp/eadkly.txt
文件存在:1
文件内容:foo bar
线:0带:“foo”是结束吗?没有
线:1带:“栏”是结束吗?没有
线:2带:“”是结束吗?是
虽然它可能看起来是直观的,PHP 5.3.9,这是:
<?php
$f = new SplFileObject('test.txt', 'r');
while (!$f->eof()) {
$f->next();
}
.
虽然是无限循环,而且永远不会退出。
下面将退出,当到达文件的结尾:
<?php
$f = new SplFileObject('test.txt', 'r');
while (!$f->eof()) {
$f->current();
}
.
SO:
$i = 0;
$fileObject2->rewind();
while (!$fileObject2->eof()) {
var_dump($fileObject2->eof());
var_dump($i++);
$fileObject2->next();
}
.
应该被重写为:
$fileObject2->rewind();
while (!$fileObject2->eof()) {
$fileObject2->current();
}
$i = $fileObject2->key();
.