【问题标题】:$file->eof() always returning false when using PHP's SplFileObject in 'r' mode$file->eof() 在 'r' 模式下使用 PHP 的 SplFileObject 时总是返回 false
【发布时间】:2012-07-29 02:28:11
【问题描述】:

为什么我的 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,所以我得到一个无限循环。

为什么会发生这些事情?我需要计算行数。

【问题讨论】:

  • “测试行”是@你的文件的唯一行?
  • @Zuul 代码完全没有任何依赖关系。您可以按原样运行它。它会在您的临时目录中创建一个文件。

标签: php fwrite eof splfileobject


【解决方案1】:

为什么会发生这些事情?

您在编写SplFileObject 类时遇到了一种特殊情况。如果不调用 next() current() 方法——使用默认 (0) 标志——迭代器永远不会向前移动。

iterator_count() 函数从不调用current();它只检查valid() 并调用next()。您的定制循环仅调用 current()next() 之一。

这应该被认为是一个错误(无论是在 PHP 本身中,还是在文档中的故障),并且以下代码应该(并且不)按预期工作。我邀请你report this misbehaviour

// 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()

回到原来的两个SplFileObjects的问题

现在应该可以按预期工作,允许附加到文件并读取其行数。

<?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));

【讨论】:

    【解决方案2】:

    已编辑 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

    这是您上面的代码,但由于文件指针而没有进入无限循环:

    <?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

    Line: 0 With: "Foo" 结束了?没有

    Line: 1 With: "Bar" 是结束吗?没有

    Line: 2 with: "" 是结束?是的

    【讨论】:

    • 使用 foreach 是一种解决方法(我一直在使用它,直到找出真正的问题为止)。 +1
    【解决方案3】:

    虽然这看起来有悖常理,但在 PHP 5.3.9 中:

    <?php
    $f = new SplFileObject('test.txt', 'r');
    while (!$f->eof()) {
        $f->next();
    }
    

    while 是一个无限循环,永远不会退出。

    当到达文件末尾时,以下内容将退出:

    <?php
    $f = new SplFileObject('test.txt', 'r');
    while (!$f->eof()) {
        $f->current();
    }
    

    所以:

    $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();
    

    【讨论】:

    • 谢谢。为什么var_dump(iterator_count($fileObject2)); 行会挂起?
    • 在测试时,它仍然挂起,即使我将 next 替换为 current
    • 什么仍然挂起? iterator_count() 电话?您似乎无法在 SPLFileObject 对象上使用此功能。看来您需要自己迭代对象。
    • eof 方法每次仍然返回 false。
    • 上面的例子清楚地显示了eof()函数确实返回false,当你使用current()函数时。这表明你有一个不同的,而不是一个 eof()
    猜你喜欢
    • 2019-03-10
    • 1970-01-01
    • 1970-01-01
    • 2020-12-27
    • 2014-08-26
    • 2012-08-23
    • 2016-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多