Если файлы не слишком огромные, то linecache модуля стандартная библиотека довольно хороша - она позволяет вам напрямую запрашивать N-ю строку такого-то файла.
Если файлы являются огромными, я рекомендую что-то вроде (предупреждение, непроверенный код):
def readlinenum(filepath, n, BUFSIZ=65536):
bufs = [None] * 2
previous_lines = lines_so_far = 0
with open(filepath, 'b') as f
while True:
bufs[0] = f.read(BUFSIZ)
if not bufs[0]:
raise ValueError('File %s has only %d lines, not %d',
filepath, lines_so_far, n)
lines_this_block = bufs[0].count('\n')
updated_lines_count = lines_so_far + lines_this_block
if n < updated_lines_count:
break
previous_lines = lines_so_far
lines_so_far = updated_lines_count
bufs[1] = bufs[0]
if n == lines_so_far:
# line split between blocks
buf = bufs[1] + bufs[0]
delta = n - previous_lines
else: # normal case
buf = bufs[0]
delta = n = lines_so_far
f = cStringIO.StringIO(buf)
for i, line in enumerate(f):
if i == delta: break
return line.rstrip()
Общая идея заключается в том, чтобы прочитать в файле как в двоичной, в больших блоках (по крайней мере, большой как самая длинная строка) - обработка (в Windows) от двоичного до «текста» дорогостоящая на огромных файлах - и используйте быстрый метод строк на большинстве блоков .count
. В конце мы можем провести синтаксический анализ строк на одном блоке (два не более в аномальном случае, когда искомая линия охватывает границы блоков).
Этот тип кода требует тщательного тестирования и проверки (который я не выполнял в этом случае), будучи склонным к ошибкам по очереди и другим границам, поэтому я бы рекомендовал его только для действительно огромных файлов - - те, которые по существу перегружают память при использовании linecache
(который просто всасывает весь файл в память вместо работы блоками). Например, на типичной современной машине с 4 Гбайт байт ОЗУ я начинаю думать о таких методах для текстовых файлов, которые находятся над GB или двумя.
Редактировать: комментатор не считает, что двоичное чтение файла происходит намного быстрее, чем обработка, требуемая текстовым режимом (только для Windows). Чтобы показать, как это неправильно, давайте воспользуемся опцией 'U'
(«универсальная новая линия»), которая заставляет обработку на конец строки также выполняться на машинах Unix (поскольку у меня нет машины Windows для ее запуска;;). Используя обычный kjv.txt файл:
$ wc kjv.txt
114150 821108 4834378 kjv.txt
(4.8 MB, 114 Klines) - около 1/1000-подобного размеров файлов я упоминал ранее:
$ python -mtimeit 'f=open("kjv.txt", "rb")' 'f.seek(0); f.read()'
100 loops, best of 3: 13.9 msec per loop
$ python -mtimeit 'f=open("kjv.txt", "rU")' 'f.seek(0); f.read()'
10 loops, best of 3: 39.2 msec per loop
т.е. примерно ровно в 3 раза для обработки линии (это на старом ноутбуке, но соотношение должно быть довольно повторяемым и в других местах).
Чтения петли на линии, конечно, еще медленнее:
$ python -mtimeit 'f=open("kjv.txt", "rU")' 'f.seek(0)' 'for x in f: pass'
10 loops, best of 3: 54.6 msec per loop
и использование readline
как комментируемые упоминался (с менее эффективной буферизацией, чем непосредственно зацикливание на файл) является худшим:
$ python -mtimeit 'f=open("kjv.txt", "rU")' 'f.seek(0); x=1' 'while x: x=f.readline()'
10 loops, best of 3: 81.1 msec per loop
Если, как говорится в этом вопросе, есть 20 000 файлов для чтения (скажем, что они все мелкие, порядка kjv.txt
), самый быстрый подход (чтение каждого файла в двоичном режиме одним глотком) должно занимать около 260 секунд, 4-5 минут es, в то время как самый медленный (на основе) должен занимать около 1600 секунд, почти полчаса - довольно значительная разница для многих, я бы сказал, наиболее реальных приложений.
Вам нужна только n-я строка файла, или вам нужны все строки до n? – danben
нужна только n-я строка – kaushik