LINUX.ORG.RU

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

 ,


1

1

есть лог файл, в котором есть метки типа:

====== CHECKTIME  2015-06-12-07:06:00
полезная информация1
полезная информация2
полезная информация3
====== CHECKTIME  2015-06-12-16:50:10
полезная информация1
полезная информация2
полезная информация3


мне надо получить последнюю такую секцию (в переменную или кортеж строк), от конца файла, желательно не перечитывая все строки от начала файла (файлы могут вырасти до 40-100мб)

понятия не имею как к этому процессу подступиться, особенно учитывая что на баше это 2 строчки:

[br]CUTLINE=`tac $file | grep --binary-files=text -n '====== CHECKTIME' | head -n1 | cut -d: -f1`[br]tail -n $CUTLINE $file[br]

★★★

Читаешь последние n байт с конца, парсишь, если не хватает, читаешь дальше. Что сложного?

panter_dsd ★★★★ ()

Не умею в питон, но, полюбому там есть возможность сикнуть файл в конец, а затем вычитывать его кусочками ища нужную метку чтобы остановиться.

deep-purple ★★★★★ ()

А еще можешь выполнить из питона этот вот твой bash-код.

aarexer ()

Поправьте если не прав, но в Python в коробке нет потокового backward stream reader или как его правильно назвать, так что:
1. Использовать supbrocess, что бы там гонять tac
2. Искать готовую реализацю bacward reader-а (с буфером и т.д.)
3. Писать его самому
4. Читать файл полностью в память и делать что-то вроде for line in reversed(file.readlines()): ...

omegatype ★★★ ()
Ответ на: комментарий от omegatype

Из коробки нет, но это достаточно популярная задачка на собеседовании :).

на баше это 2 строчки

У тебя там grep читает весь файл целиком.

Короче, ты реши действительно у тебе так важна скорость что прочитать файлы целиком это проблема. Потому что, скажем, раз в час распарсить лог на 100метров это НЕ проблема.

Если всё же проблема то читай чанками с конца. Готовые решения вполне гуглятся. Вот, например, тут есть итератор которые строки в обратном порядки из файла достаёт: http://stackoverflow.com/questions/260273/most-efficient-way-to-search-the-la... . Ящитаю, это отличное решение.

true_admin ★★★★★ ()

Ну башевский этот код будет читать весь файл. Греп весь же прочитает. Можно так же. Делаешь пустой список. Открываешь файл. Читаешь по одной строке (readline). Если строка — метка, то список очищаешь (делаешь пустым []), иначе, вносишь строку в список. При EOF у тебя в списке будут все строки после последней метки.

Если важно именно не читать весь файл (греп будет это делать), то открываешь файл, сикаешь на -1024 от конца, читаешь. Метка есть? Сплитаешь по ней, берёшь конец. Нет? Сикаешь на -2048 от конца, и так далее. Ну цифры можно примерно прикинуть из того, сколько обычно инфы в одной такой секции.

Но учитывая, что файл может быть всего-то 100мб, можно и весь прочитать в строку, да сплитнуть по меткам.

Psych218 ★★★★★ ()
Последнее исправление: Psych218 (всего исправлений: 2)
Ответ на: комментарий от true_admin

У тебя там grep читает весь файл целиком.

tac в большинстве случаев читает файл нецеликом, может быть и целиком, но лишь до момента пока grep не найдет что надо, и head -n1 обрубит пайп.
еще конечно tail перечитает файл, но примерно также как и tac+grep+head.

раз в час распарсить лог на 100метров это НЕ проблема.

у меня три сотни этих логов, и как правило парсинг должен запускаться практически одновременно раза 3-4 в час, не охота погибнуть от руки oomkiller-а.

bl ★★★ ()
Ответ на: комментарий от bl

Ну значит, как уже многие сказали выше, сикай файл в конец и читай кусочками пока не встретишь метку. А я бы еще и лимит ввел, например если метка не нашлась в куске N байт с конца файла, тогда значит неформат.

deep-purple ★★★★★ ()
Ответ на: комментарий от bl

Ну что-то типа такого. Переходим в конец, откатываемся «вверх» на 1024, читаем, находим все последнюю «метку» в прочитанном - с нее и будет наша нужная информация. Это при условии, что мы за один раз прочитаем хотя бы один кусок

====== CHECKTIME  2015-06-12-16:50:10
полезная информация1
полезная информация2
полезная информация3
Если мы не сможем сделать этого - возможно два пути.

1) Переделать все и читать вообще построчно(что правильнее)

2) Просто увеличить число считанных байт.

#!/usr/local/bin/python3.4
#coding=utf-8
with open("example.txt", "r") as f:
	f.seek(0, 2)
	fsize = f.tell()
	f.seek(max(fsize - 1024, 0), 0)
	lines = f.readlines()

for x in lines[::-1]:
	if x.startswith("====== CHECKTIME"):
		start = lines.index(x)
		break


print(lines[start + 1:])

Плюс как уже советовали можно ввести метку о не формате.

aarexer ()
Последнее исправление: aarexer (всего исправлений: 1)
Ответ на: комментарий от true_admin

Вот, например, тут есть итератор которые строки в обратном порядки из файла достаёт: http://stackoverflow.com/questions/260273/most-efficient-way-to-search-the-la... . Ящитаю, это отличное решение.

оказывается уже смотрел его, но что то не понял как оно работает, буду думать.

bl ★★★ ()
Ответ на: комментарий от bl

Я имел в виду вот это решение: http://stackoverflow.com/a/260433 .

Оно читает файл в обратном направлении в буфер чанками. Буфер зовётся part. Как только доходит до '\n' оно переворачивает буфер (part[::-1] тоже самое что и reversed(part)).

Как только дошли до конца текущего чанка мы считываем следующий. При этом буфер не сбрасывается.

Кстати, можешь работать с файлами через mmap как с массивом char. Тогда всё будет ещё очевиднее.

Вообще, эту задачку мне пару раз давали на собеседованиях :).

true_admin ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.