Чтение файла с конца

Чтение файла с конца

Любое приложение генерирует информацию о ходе своей работы. Как правило эта информация скапливается в лог-файлах. Для контроля работы приложения приходится регулярно эти логи читать. Для широко распространенных продуктов пишут анализаторы, которые позволяют более удобно читать логи и агрегировать скопившуюся информацию. Нужно, что бы программа прочитала файл, вычленила из него самую полезную информацию и показала ее разработчику. Записей в логе может быть много и самих файлов может быть тоже много. Поэтому необходимо создать страницу, которая сможет вывести по несколько последних записей из каждого файла.

Представим, что есть лог-файл, в который постоянно добавляются записи. Нам нужно получить, скажем, последние три. Засовывать файл в память с помощью например file_get_contents не рационально. В некоторых случаях памяти может просто не хватить. Обходить весь файл построчно тоже не интересно, потому что программе придется просмотреть каждую строчку, пока она доберется до конца. И как в этом случае узнать, что это три последние записи. Такая проблема встала передо мной когда я писал свой парсер логов для Yii2. Гугл мне ничего не ответил и я смог найти ответ в документации.

Чудесная функция fseek принимает вторым аргументом число - отступ от начала, равный количеству байт, на которое надо сдвинуть курсор. В текстовом редакторе, курсор работает по следующему принципу. Когда вы ставите его в самое начало документа, то его положение равно нулю. Стоит нажать один раз вправо на клавиатуре - курсор смещается на один символ и его положение уже равно единице. Здесь мы действуем с обратной стороны. Мы указываем число, на которое курсору надо сместиться. Курсор находит указанное место, и мы можем прочитать нужную строку.

Так как быстро прочитать три записи с конца? Функция принимает третий необязательный аргумент whence (константу), который влияет на результат указания офсета.

  1. SEEK_SET - устанавливает курсор на указанное количество байт от начала файла.
  2. SEEK_CUR - устанавливает курсор на указанное количество байт от текущего положения.
  3. SEEK_END - устанавливает курсор на указанное количество байт от конца файла.

Так вот, последняя константа нам и нужна. Её то я и использовал. Но оффсет здесь должен быть отрицательным. Если вы хотите сместить курсор на 10 байт с конца, то вызывать ее нужно следующим образом:

fseek($handle, -10, SEEK_END);

Обратите внимание, что при неудачном смещении функция вернет не false а -1. Для своего модуля который читает логи я написал функцию, которая смещает курсор на одну строчку:

    public function seek()
    {
        while(true)
        {
            $this->length--;
            if(fseek($this->handle, $this->length, SEEK_END) == -1) return false;
            if(fgetc($this->handle) == "\n") return true;
        }
    }

Она сдвигает курсор к началу файла на один символ и смотрит, что это за символ. Если это перенос строки, то значит смещение завершено. Таким образом я могу прочитать три записи с конца файла.

Редактор: Татьяна Романова