2015-01-14 5 views
3

Я работаю над странной проблемой PHP в последние несколько дней, когда функция feof() возвращает true до конца файла. Ниже приведен скелет моего кода: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 
    } 
} 

Через много испытаний я обнаружил, что программа отлично на все работы, кроме одного файла:

  • Файл хранится на локальном диске.
  • Этот файл содержит около 8 миллионов строк, усредняющих где-то около 200-500 символов в строке.
  • Он уже был очищен и под тщательным изучением с шестнадцатеричным редактором, никаких аномальных символов не обнаружено.
  • Программа последовательно терпит неудачу в строке 7172714, когда она считает, что она достигла конца файла (хотя у него есть ~ 800K строк слева).
  • Я протестировал программу на файлы, в которых было меньше символов в строке, но было между 20-30 миллионами строк без проблем.
  • Я попытался запустить код из комментария на http://php.net/manual/en/function.fgets.php, чтобы узнать, не было ли в этом коде что-то, что вызывало проблему, а сторонний код провалился в той же строке. EDIT: также стоит упомянуть, что сторонний код использовал fread() вместо fgets().
  • Я попытался указать несколько размеров буфера в функции fgets, и ни одна из них не имела никакого значения.

Выход из var_dump ($ мета) выглядит следующим образом:

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 вернуть истинный до конца файла, я должен предположить, что либо :

A) что-то, что вызывает поток FOPEN на провал, и тогда ничто не может быть прочитан (вызывая feof вернуть истинный)

B) Существует некоторый буфер где-то, что заполнение и вызывает хаос

C) Боги PHP злится

Я искал повсюду, чтобы увидеть, если кто-то еще с этой проблемой и не могу найти какие-либо случаи, за исключением C++, где файл читается в виде текстового режима вместо и вызывал проблему.

UPDATE: У меня был мой скрипт, который постоянно выводил количество повторных попыток чтения и уникальный идентификатор пользователя, связанный с записью, найденной рядом с ним. Сценарий по-прежнему не работает после строки 7172713 из 7175502, но уникальный идентификатор последнего пользователя в файле отображается в строке 7172713. Кажется, что проблема по какой-то причине пропускается и не читается. Все разрывы строк присутствуют.

+0

Возможно ли, что в php закончилось чтение файла? –

+0

Вспомним, что функция чтения вызывается для блоков строк. Он считывает 500 строк, выполняет некоторую обработку и возвращает значение и сохраняет последнее местоположение в переменной класса. В следующий раз, когда он называется, он читает следующие 500 строк, начиная с которых он остановился, используя переменную класса. Все правильно рассмотрено с использованием unset и при мониторинге использования памяти сервера я не заметил ничего ненормального. Поскольку это было слишком сложно для проведения тестирования, я написал этот код и просто отключил строку, прочитанную в успешной строке. Все еще видя ту же проблему. – user2395126

+0

вы пытались использовать 'rb' = ** читать двоичный код ** вместо просто' r'? –

ответ

2

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

?> 

UPDATE: После нескольких часов больше поиска, анализа, таскание за волосы, и т.д., кажется, что преступник был неперехваченное плохой характер - в этом случае шестнадцатеричное значение 1/2 символа BD. Создавая файл, который я читал из сценария, использовал 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 
    } 
} 
4

вы должны разделить файл или увеличить время ожидания в 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

+0

Таймаут установлен на 72 часа, а upload_max_filesize - на 50G. Также стоит упомянуть ограничение памяти, равное 2048 МБ. – user2395126

+0

может быть ваш файл закрыт по соображениям безопасности Антивирусом или брандмауэром –

+0

Я думал об этом, отключил все и не повезло. Запустите скрипт с привилегиями root, чтобы узнать, поможет ли это, а также не повезло. – user2395126

Смежные вопросы