Прочитать последнюю строчку файла
Я столкнулся с проблемой. У меня есть журнал в ящике Linux, в котором записываются результаты нескольких запущенных процессов. Этот файл иногда может стать очень большим, и мне нужно прочитать последнюю строку из этого файла.
Проблема в том, что это действие будет вызываться через запрос AJAX довольно часто, и когда размер файла этого журнала превышает 5-6 МБ, это не очень хорошо для сервера. Итак, я думаю, что мне нужно прочитать последнюю строку, но не читать весь файл и проходить через него или загружать его в ОЗУ, потому что это просто загрузит до смерти мой ящик.
Есть ли какая-нибудь оптимизация для этой операции, чтобы она работала без сбоев и не повредила серверу или не убила Apache?
Другой вариант, который у меня есть, - exec('tail -n 1 /path/to/log') , но это звучит не очень хорошо.
Позднее отредактируйте: Я НЕ хочу помещать файл в ОЗУ, потому что он может стать огромным. fopen() не вариант.
Это должно работать:
Обратите внимание, что это решение будет повторять последний символ строки, если ваш файл не заканчивается новой строкой. Если ваш файл не заканчивается новой строкой, вы можете изменить оба экземпляра $cursor-- на --$cursor .
Используйте fseek. Вы стремитесь к последней позиции и ищете ее назад (используйте ftell, чтобы укажите текущую позицию), пока не найдете "\ n".
ПРИМЕЧАНИЕ: я не тестировал. Вам может потребоваться некоторая корректировка.
ОБНОВЛЕНИЕ: Спасибо Syntax Error за указание на пустой файл.
ОБНОВЛЕНИЕ 2: исправлена еще одна синтаксическая ошибка, отсутствует точка с запятой в $LastLine = ""
Вы ищете функцию fseek. В разделе комментариев есть рабочие примеры того, как читать последнюю строку файла.
Это код Иону Г. Стэна
Я немного изменил ваш код и сделал его функцией для повторного использования
Вы получите эту последнюю строчку
Если вы знаете верхнюю границу длины строки, вы можете сделать что-то вроде этого.
В ответ на редактирование : fopen просто получает дескриптор файла (т.е. убедитесь, что он существует, у процесса есть разрешение, позволяет операционной системе узнать, что процесс использует файл и т. д.). В этом примере в память будут считаны только 1024 символа из файла.
Ваша проблема похожа на эту
Лучший способ избежать загрузки всего файла в память выглядит так:
Можно ли было бы оптимизировать это с другой стороны? Если это так, просто позвольте приложению ведения журнала всегда записывать строку в файл при ее усечении (т.е.> вместо >>)
Некоторая оптимизация может быть достигнута путем «угадывания», однако, просто откройте файл и по средней ширине строки журнала вы сможете угадать, где будет последняя строка. Перейдите в эту позицию с помощью fseek и найдите последнюю строку.
Это мое решение только с одним циклом
Эта функция позволит вам прочитать последнюю строку или (необязательно) весь файл построчно с конца, передав $initial_pos , который укажет, с чего начать чтение файла с конца (отрицательное целое число).
Затем, чтобы прочитать последнюю строку:
Чтобы прочитать весь файл построчно с конца:
Перемещает фрагмент файла назад фрагмент за фрагментом, останавливается после нахождения новой строки и возвращает все, что находится после последнего "\ n".
Я хотел бы прочитать только последнюю строку текстового файла (я на UNIX, могу использовать Boost). Все методы, которые я знаю, требуют сканирования всего файла, чтобы получить последнюю строку, которая вообще не эффективна. Есть ли эффективный способ получить только последнюю строку?
кроме того, мне нужно, чтобы это было достаточно надежным, чтобы он работал, даже если текстовый файл, о котором идет речь, постоянно добавляется другим процессом.
используйте seekg, чтобы перейти к концу файла, затем прочитайте назад, пока не найдете первую новую строку. Ниже приведен пример кода с верхней части моей головы с помощью MSVC.
и ниже приведен тестовый файл. Он успешно работает с пустыми, однострочными и многострочными данными в текстовом файле.
перейти к концу, и начать чтение блоков назад, пока вы не найдете все ваши критерии для линии. Если последний блок не" заканчивается " строкой, вам, вероятно, придется попробовать сканировать вперед (предполагая, что действительно длинная строка в активно добавляемом файле).
вы можете использовать seekg (), чтобы перейти к концу файла и прочитать назад, псевдо-код выглядит так:
хотя ответ derpface определенно правильный, он часто возвращает неожиданные результаты. Причина этого в том, что, по крайней мере, в моей операционной системе (Mac OSX 10.9.5), многие текстовые редакторы завершают свои файлы символом "end line".
например, когда я открываю vim, введите только один символ "a" (без возврата) и сохраните, файл теперь будет содержать (в шестнадцатеричном формате):
где 61-буква "a" , а 0A-конец строки характер.
это означает, что код derpface вернет пустую строку для всех файлов, созданных таким текстовым редактором.
хотя я, конечно, могу представить случаи, когда файл, завершенный "конечной строкой", должен возвращать пустую строку, я думаю, что игнорирование последнего символа "конечной строки" было бы более уместным при работе с обычными текстовыми файлами; если файл завершается символом "конечной строки", мы правильно его игнорируем, и если файл не завершается символом " конца характер линии нам не нужно проверять.
мой код для игнорирования последнего символа входной файл:
что будет на выходе:
в одном файле "a".
EDIT: строка if((int)fin.tellg() 2 ГБ), потому что tellg не просто возвращает количество символов с начала файла (функция tellg () дает неправильный размер файла?). Может быть лучше отдельно тест для запуска файла fin.tellg()==tellgValueForStartOfFile и ошибок fin.tellg()==-1 . The tellgValueForStartOfFile , вероятно, 0, но лучший способ убедиться, вероятно, будет:
Я также боролся с проблемой, потому что я запустил код убервулу, а также получил пустую строку. Вот что я нашел. Я использую следующее .CSV-файл в качестве примера:
чтобы понять команды в коде, обратите внимание на следующие местоположения и их соответствующие символы. (Loc, char) : . (63,'3') , (64,'5') , (65,-) , (66,'\n'), (EOF, -).
первоначально это было предназначено для чтения последней записи системного журнала. Учитывая, что последний символ перед EOF '\n' мы ищем, чтобы найти следующее вхождение '\n' и затем мы храним строку в строку.
в моем приложении PHP мне нужно чтение нескольких строк, начиная с конца много файлов (преимущественно журналы). Иногда мне нужен только последний, иногда мне нужен . десятки или сотни. В принципе, я хочу что-то гибкое, как Unix tail команда.
здесь есть вопросы о том, как получить последнюю строку из файла (но Мне нужно N lines), и были даны различные решения. Я не уверен, что один из лучших и что выступить лучше.
методы обзор
поиск в Интернете, я наткнулся на различные решения. Я могу их сгруппировать. в трех подходах:
- наивный С file() функции PHP;
- измена те, что работает в системе;
- могучий те, которые счастливо прыгать вокруг открытого файла с помощью fseek() .
в итоге я выбрал (или написал) пять решения,наивный один, a измена один и три могучий те.
все решения работа. В том смысле, что они возвращают ожидаемый результат от любой файл и любое количество строк, которые мы запрашиваем (за исключением решения №1, которое может разрыв ограничений памяти PHP в случае больших файлов, ничего не возвращающих). Но кто лучше?
заключение
избежать Решение № 1 если вы следует читать файлы размером более 10 КБ.
Первое требование . Я хочу прочитать последнюю строку файла и присвоить последнее значение переменной в Python.
Второе требование -
Вот мой пример файла.
Из этого файла я хочу прочитать содержимое, например filename.txt , которое будет после
Простое решение, которое не требует сохранения всего файла в памяти (например, с помощью file.readlines() или аналогичной конструкции):
Для больших файлов было бы более эффективно искать до конца файла и двигаться назад, чтобы найти новую строку, например:
Обратите внимание, что файл должен быть открыт в двоичном режиме, иначе будет невозможно искать с конца.
Подход seek() невероятно быстр, но не так легко понять, что он делает. Подход с использованием цикла for не слишком медленный и простой для понимания. Однако подход readlines() к следующему по количеству голосов ответу немного быстрее, чем подход for здесь.
S Если вы попробуете его на достаточно большом файле (например, несколько мегабайт), file.readlines() будет медленнее, чем простой цикл for . При работе с файлами большего размера может быть исчерпана доступная память.
Это прекрасный ответ для получения последней строки файла. Как правило, мне нужна не последняя строка, а вторая или третья последняя строка файла, поэтому я попытался обобщить это в функции, изменив -2 на '' -n ", но я получаю переменные ответы и думаю это потому, что он в байтах, и я не совсем понял код должным образом. n = 2 дает последнюю строку, а n = 3 действительно дает мне 2-ю последнюю строку, но n = 4 дает мне 9-ю последнюю строку. что я здесь сделал не так?
Я бы подумал, что вы не захотите менять -2, я считаю, что они перемещают вас по одному символу за раз (в сочетании с f.read (1) . если вы измените их на большие значения, вы пропускать символы, и это приведет к ошибочным результатам. Вместо этого я бы изменил цикл while, включив в него количество совпадений, чтобы он выполнялся до тех пор, пока не будет совпадать n раз. (в основном, пока совпадение
Почему бы вам просто не прочитать все строки и не сохранить последнюю строку в переменной?
Это хороший подход. Это не очень медленно, его легче понять, чем подход seek() , и когда он заключен в блок with , нет необходимости явно вызывать close() в файле.
Насколько это эффективно? Если в вашем файле миллионы строк, вам придется прочитать миллионы строк. Все, кроме seek или метода, который начинается в конце файла, крайне неэффективно.
В системах, где есть команда tail , вы можете использовать tail , что для больших файлов избавит вас от необходимости читать файл целиком.
Примечание: команда decode() требуется только для python3
Подойдет для python2.x
Он не просто спрашивает, как читать строки в файле или как читать последнюю строку в переменной. Он также спрашивает, как разобрать подстроку из последней строки, содержащую его целевое значение.
Вот один способ. Это самый короткий путь? Нет, но если вы не знаете, как нарезать строки, вам следует начать с изучения каждой встроенной функции, используемой здесь. Этот код получит то, что вы хотите:
Вывод команды печати должен выглядеть так:
- Чтение текстовых файлов
- Составление списка Python строк из содержимого, чтобы упростить получение последней строки с использованием индекса len(List) -1 с отсчетом от нуля.
- Использование find для получения позиций индекса строки внутри строки
- Использование slice для получения подстрок
Все эти темы находятся в документации Python - здесь нет ничего лишнего, и импорт не требуется для использования встроенных функций, которые здесь использовались.
Я хочу прочитать последние n строк очень большого файла, не читая весь файл в любой буфер / область памяти с помощью Java.
Я осмотрел API JDK и Apache Commons I / O и не смог найти тот, который подходит для этой цели.
Я думал о том, как хвост или меньше делает это в UNIX. Я не думаю, что они загружают весь файл, а затем показывают последние несколько строк файла. Должен быть аналогичный способ сделать то же самое и в Java.
если вы используете RandomAccessFile , вы можете использовать length и seek чтобы попасть в конкретную точку в конце файла, а затем читать дальше.
если вы обнаружите, что было недостаточно линий, отступите с этой точки и повторите попытку. Как только вы выяснили, где N - Я последняя строка начинается, вы можете искать там и просто читать и печатать.
первоначальное предположение наилучшего предположения может быть сделано на основе ваших данных свойства. Например, если это текстовый файл, возможно, длина строки не будет превышать в среднем 132, поэтому, чтобы получить последние пять строк, начните 660 символов до конца. Затем, если вы ошиблись, попробуйте еще раз в 1320 (вы даже можете использовать то, что вы узнали из последних 660 символов, чтобы настроить это-пример: если эти 660 символов были всего три строки, следующая попытка может быть 660 / 3 * 5, плюс, может быть, немного больше на всякий случай).
Я нашел самый простой способ сделать с помощью ReversedLinesFileReader С Apache commons-io API-интерфейс. Этот метод даст вам строку снизу вверх файла, и вы можете указать n_lines значение для указания количества линий.
RandomAccessFile-хорошее место для начала, как описано в других ответах. Есть один важный нюанс хотя.
если ваш файл не закодирован с кодировкой один байт на символ, readLine() метод не будет работать для вас. И readUTF() не будет работать ни при каких обстоятельствах. (Он читает строку, которой предшествует число символов . )
вместо этого вам нужно будет убедиться, что вы ищете маркеры конца строки таким образом, чтобы уважать границы символов кодировки. Для кодировок фиксированной длины (например, вкусы UTF-16 или UTF-32) необходимо извлечь символы, начиная с байтовых позиций, которые делятся на размер символов в байтах. Для кодировок переменной длины (например, UTF-8) вам нужно найти байт, который должны быть первым байтом символа.
в случае UTF-8 первым байтом символа будет 0xxxxxxx или 110xxxxx или 1110xxxx или 11110xxx . Все остальное - либо второй / третий байт, либо незаконная последовательность UTF-8. См.Стандарт Unicode, Версия 5.2, Глава 3.9, таблица 3-7. Это означает, как указывает обсуждение комментариев, что любые байты 0x0A и 0x0D в правильно закодированном потоке UTF-8 будут представлять символ LF или CR. Таким образом, подсчет байтов является допустимой стратегией реализации (для UTF-8).
определив правильную границу символа, вы можете просто позвонить new String(. ) передача массива байтов, смещение, количество и кодирование, а затем повторно вызовите String.lastIndexOf(. ) для подсчета конца строк.
нашел RandomAccessFile и другие классы чтения буферов слишком медленные для меня. Ничто не может быть быстрее, чем tail - . Так что это было лучшим решением для меня.
обратите внимание, что в Apache Commons Collections 4 Этот класс, похоже, был переименован в CircularFifoQueue
У меня была аналогичная проблема, но я не понял других решений.
я использовал это. Надеюсь, это простой код.
вот лучший способ я нашел, чтобы сделать это. Простой и довольно быстрый и оперативную память.
тесты производительности
чтобы ответить на вопрос, я запускаю тесты. Вот как это бывает. сделано, не так ли?
я подготовил образец 100 КБ объединение различных файлов, найденных в мой . Тогда я написал PHP-скрипт, который использует каждый из пять решений для извлечения 1, 2, . 10, 20, . 100, 200, . 1000 строки из конца файла. Каждый тест повторяется десять раз (это что-то вроде 5 × 28 × 10 = 1400 испытаний), измерений средняя продолжительность время in микросекунды.
я запускаю скрипт на своей локальной машине разработки (Xubuntu 12.04, PHP 5.3.10, 2.70 GHz двухъядерный процессор, 2 ГБ ОЗУ) с помощью командной строки PHP переводчик. Вот результаты:
давайте попробуем с большим файлом. Что, если нам придется читать 10 МБ лог-файл?
и для крошечных файлов журнала? Это график для 10 КБ :
вы можете скачать все мои тестовые файлы, источники и результаты здесь.
Читайте также: