Последние несколько дней я работал над странной проблемой PHP, когда функция feof () возвращает true до конца файла. Ниже приведен скелет моего кода:
$this->fh = fopen("bigfile.txt", "r");
while(!feof($this->fh))
{
$dataString = fgets($this->fh);
if($dataString === false && !feof($this->fh))
{
echo "Error reading file besides EOF";
}
elseif($dataString === false && feof($this->fh))
{
echo "We are at the end of the file.\n";
//check status of the stream
$meta = stream_get_meta_data($this->fh);
var_dump($meta);
}
else
{
//else all is good, process line read in
}
}
После многочисленных испытаний я обнаружил, что программа отлично работает на всем, кроме одного файла:
Вывод из var_dump ($ meta) выглядит следующим образом:
array(9) {
["wrapper_type"]=>
string(9) "plainfile"["stream_type"]=>
string(5) "STDIO"["mode"]=>
string(1) "r"["unread_bytes"]=>
int(0)
["seekable"]=>
bool(true)
["uri"]=>
string(65) "full path of file being read"["timed_out"]=>
bool(false)
["blocked"]=>
bool(true)
["eof"]=>
bool(true)
}
Пытаясь выяснить, что заставляет feof возвращать true до конца файла, я должен догадаться, что либо:
A) Что-то вызывает сбой потока fopen, и тогда ничего не может быть прочитано (в результате чего feof возвращает true)
Б) Где-то есть буфер, который заполняет и вызывает хаос
C) PHP боги злы
Я искал повсюду, чтобы увидеть, если кто-то еще имел эту проблему и не может найти какие-либо экземпляры, кроме как в C ++, где файл читался в текстовом режиме вместо двоичного режима и вызывал проблему.
ОБНОВИТЬ:
Мой скрипт постоянно выводил число повторений функции чтения и уникальный идентификатор пользователя, связанный с записью, найденной рядом с ним. Сценарий все еще не выполняется после строки 7172713 из 7175502, но уникальный идентификатор последнего пользователя в файле отображается в строке 7172713. Кажется, что по какой-то причине строки пропускаются и не читаются. Все разрывы строк присутствуют.
Вы должны разделить свой файл или увеличить время ожидания в php
от:
upload_max_filesize = 2M
;or whatever size you want
max_execution_time = 60
; Кроме того, выше, если вы должны
так как:
Возвращает TRUE, если указатель файла находится в EOF или произошла ошибка (включая время ожидания сокета); в противном случае возвращает FALSE.
увидеть:http://php.net/manual/en/function.feof.php
fgets (), по-видимому, случайно читает в некоторых строках, которые имеют пустой контент. Сценарий фактически доходит до конца файла, хотя мой тест, который показал, что читаемые номера строк отставал из-за того, как я выполнял проверку ошибок (и как проверка ошибок была написана в стороннем коде). Теперь реальный вопрос заключается в том, что заставляет fgets () и fread () думать, что строка пуста, хотя это не так. Я задам это как отдельный вопрос, так как это изменение темы. Спасибо за вашу помощь!
Кроме того, просто так никто не остается зависшим, причина того, что сторонний код не работал, заключается в том, что он полагался на строку, по крайней мере, с разрывом строки, когда текущая проблема с fgets и fread, возвращающими пустую строку, не дает сценарию ему нужно знать, какая строка когда-либо существовала, поэтому он продолжает пытаться выполнить после конца файла. Ниже приведен слегка измененный сторонний скрипт, который я до сих пор считаю отличным, учитывая его скорость выполнения.
Оригинальный скрипт можно найти в комментариях здесь: http://php.net/manual/en/function.fgets.php и я не беру на это абсолютно никакой ответственности.
<?php
//File to be opened
$file = "/path/to/file.ext";
//Open file (DON'T USE a+ pointer will be wrong!)
$fp = fopen($file, 'r');
//Read 16meg chunks
$read = 16777216;
//\n Marker
$part = 0;
while(!feof($fp))
{
$rbuf = fread($fp, $read);
for($i=$read;$i > 0 || $n == chr(10);$i--)
{
$n=substr($rbuf, $i, 1);
if($n == chr(10))break;
//If we are at the end of the file, just grab the rest and stop loop
elseif(feof($fp))
{
$i = $read;
$buf = substr($rbuf, 0, $i+1);
echo "<EOF>\n";
break;
}
}
//This is the buffer we want to do stuff with, maybe thow to a function?
$buf = substr($rbuf, 0, $i+1);
//output the chunk we just read and mark where it stopped with <break>
echo $buf . "\n<break>\n";
//Point marker back to last \n point
$part = ftell($fp)-($read-($i+1));
fseek($fp, $part);
}
fclose($fp);
?>
ОБНОВЛЕНИЕ: После долгих часов поисков, анализа, выдергивания волос и т. Д. Кажется, что преступник был непостижимым плохим персонажем — в данном случае шестнадцатеричное значение BD в 1/2 символа. При создании файла, который я читал из скрипта, использовал stream_get_line (), чтобы прочитать строку из его оригинального источника. Затем он должен был удалить все плохие символы (кажется, что мое регулярное выражение было не на должном уровне), а затем использовать str_getcsv () для преобразования содержимого в массив, выполнить некоторую обработку, а затем записать в новый файл (тот, который я был пытаюсь читать). Где-то в этом процессе, вероятно, str_getcsv (), символ 1/2 заставил целую вещь просто вставить пустую строку вместо данных. Несколько тысяч из них были размещены по всему файлу (везде, где появился символ 1/2). Это делало файл правильной длины, но EOF слишком быстро достигался при подсчете входных данных на основе известного числа строк. Я хочу поблагодарить всех, кто помог мне с этой проблемой, и мне очень жаль, что реальная причина не имела никакого отношения к моему вопросу. Однако, если бы не все предложения и вопросы, я бы не посмотрел в нужных местах.
Урок, извлеченный из этого опыта — когда EOF достигается слишком быстро, лучше всего искать случаи двойных разрывов строк. При написании сценария, который читает из отформатированного файла, рекомендуется проверять их. Ниже мой оригинальный код, модифицированный для этого:
$this->fh = fopen("bigfile.txt", "r");
while(!feof($this->fh))
{
$dataString = fgets($this->fh);
if($dataString == "\n" || $dataString == "\r\n" || $dataString == "")
{
throw new Exception("Empty line found.");
}
if($dataString === false && !feof($this->fh))
{
echo "Error reading file besides EOF";
}
elseif($dataString === false && feof($this->fh))
{
echo "We are at the end of the file.\n";
//check status of the stream
$meta = stream_get_meta_data($this->fh);
var_dump($meta);
}
else
{
//else all is good, process line read in
}
}