$file->eof() всегда возвращает false при использовании PHP SplFileObject в режиме 'r'
Почему мой 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, поэтому я получаю бесконечный цикл.
Почему эти вещи происходят? Мне нужно получить количество строк.
3 ответа
Почему эти вещи происходят?
Вы испытываете особенность в том, что SplFileObject
класс написан. Без звонка next()
а также current()
методы - с использованием по умолчанию (0
) flags - итератор никогда не двигается вперед.
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
Это ваш код выше, но без вхождения в бесконечные циклы из-за указателя файла:
<?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();
}
Так:
$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();