Как перейти к определенной строке в огромном текстовом файле? - PullRequest
98 голосов
/ 06 марта 2009

Есть ли альтернативы коду ниже:

startFromLine = 141978 # or whatever line I need to jump to

urlsfile = open(filename, "rb", 0)

linesCounter = 1

for line in urlsfile:
    if linesCounter > startFromLine:
        DoSomethingWithThisLine(line)

    linesCounter += 1

Если я обрабатываю огромный текстовый файл (~15MB) со строками неизвестной, но разной длины, и мне нужно перейти к определенной строке, какое число я знаю заранее? Я чувствую себя плохо, обрабатывая их один за другим, когда знаю, что могу игнорировать хотя бы первую половину файла. Ищете более элегантное решение, если оно есть.

Ответы [ 15 ]

104 голосов
/ 07 марта 2009

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

# Read in the file once and build a list of line offsets
line_offset = []
offset = 0
for line in file:
    line_offset.append(offset)
    offset += len(line)
file.seek(0)

# Now, to skip to line n (with the first line being line 0), just do
file.seek(line_offset[n])
27 голосов
/ 06 марта 2009

linecache

Модуль linecache позволяет получить любую строку из исходного файла Python, пытаясь при этом внутренне оптимизировать, используя кэш, общий случай, когда многие строки читаются из одного файла. Это используется модулем traceback для извлечения исходных строк для включения в отформатированную трассировку ...

20 голосов
/ 07 марта 2009

У вас не так много вариантов, если строки имеют разную длину ... вам, к сожалению, нужно обработать символы конца строки, чтобы узнать, когда вы переходите к следующей строке.

Однако вы можете значительно ускорить это и сократить использование памяти, изменив последний параметр на «open» на значение, отличное от 0.

0 означает, что операция чтения файла небуферизована, что является очень медленным и интенсивным диском. 1 означает, что файл буферизован строкой, что является улучшением. Все, что выше 1 (скажем, 8k .. т.е.: 8096 или выше), считывает фрагменты файла в память. Вы по-прежнему обращаетесь к нему через for line in open(etc):, но python работает только немного, отбрасывая каждый буферизованный кусок после его обработки.

13 голосов
/ 07 марта 2009

Я, наверное, избалован обильным бараном, но 15 м не огромен. Чтение в память с readlines() - это то, что я обычно делаю с файлами такого размера. Доступ к строке после этого тривиален.

5 голосов
/ 07 марта 2009

Поскольку нет способа определить длину всех строк, не читая их, у вас нет выбора, кроме как перебрать все строки перед вашей стартовой строкой. Все, что вы можете сделать, это сделать это красиво. Если файл действительно большой, вы можете использовать генераторный подход:

from itertools import dropwhile

def iterate_from_line(f, start_from_line):
    return (l for i, l in dropwhile(lambda x: x[0] < start_from_line, enumerate(f)))

for line in iterate_from_line(open(filename, "r", 0), 141978):
    DoSomethingWithThisLine(line)

Примечание: в данном подходе индекс равен нулю.

4 голосов
/ 26 апреля 2016

Я удивлен, что никто не упомянул остров

line = next(itertools.islice(Fhandle,index_of_interest,index_of_interest+1),None) # just the one line

или, если вы хотите, весь остальной файл

rest_of_file = itertools.islice(Fhandle,index_of_interest)
for line in rest_of_file:
    print line

или, если вы хотите, чтобы каждая вторая строка из файла

rest_of_file = itertools.islice(Fhandle,index_of_interest,None,2)
for odd_line in rest_of_file:
    print odd_line
3 голосов
/ 06 июля 2014

У меня была такая же проблема (нужно извлечь из огромной строки файла).

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

Я узнал следующее решение: Сначала я заполнил словарь начальной позицией каждой строки (ключ - номер строки, а значение - накопленная длина предыдущих строк).

t = open(file,’r’)
dict_pos = {}

kolvo = 0
length = 0
for each in t:
    dict_pos[kolvo] = length
    length = length+len(each)
    kolvo = kolvo+1

в конечном счете, целевая функция:

def give_line(line_number):
    t.seek(dict_pos.get(line_number))
    line = t.readline()
    return line

t.seek (line_number) - команда, которая выполняет удаление файла до начала строки. Итак, если вы в следующий раз выполните readline - вы получите целевую строку.

Используя такой подход, я сэкономил значительную часть времени.

3 голосов
/ 28 апреля 2010

Что генерирует файл, который вы хотите обработать? Если это что-то под вашим контролем, вы можете сгенерировать индекс (какая строка находится в какой позиции.) Во время добавления файла. Индексный файл может иметь фиксированный размер строки (с пробелами или числами с 0) и определенно будет меньше. И, таким образом, могут быть прочитаны и обработаны быстро.

  • Какую строку вы хотите?
  • Вычислить смещение байта соответствующего номера строки в индексном файле (возможно, потому что размер строки индексного файла постоянен).
  • Используйте поиск или что-то еще, чтобы напрямую перейти к строке из файла индекса.
  • Разобрать, чтобы получить смещение в байтах для соответствующей строки фактического файла.
3 голосов
/ 07 марта 2009

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

Конечно, все зависит от того, что вы пытаетесь сделать, и от того, как часто вы будете перепрыгивать через файл.

Например, если вы собираетесь прыгать по строкам много раз в одном и том же файле, и вы знаете, что файл не изменяется при работе с ним, вы можете сделать это:
Во-первых, пройдите весь файл и запишите «положение поиска» некоторых номеров ключевых строк (например, когда-либо 1000 строк),
Затем, если вы хотите строку 12005, перейдите на позицию 12000 (которую вы записали), затем прочитайте 5 строк, и вы узнаете, что находитесь на линии 12005 и так далее

3 голосов
/ 07 марта 2009

Если вы заранее знаете позицию в файле (точнее номер строки), вы можете использовать file.seek () , чтобы перейти на эту позицию.

Редактировать : вы можете использовать функцию linecache.getline (filename, lineno) , которая будет возвращать содержимое строки lineno, но только после считывания всего файла в память , Хорошо, если вы случайным образом обращаетесь к строкам из файла (как это может сделать сам Python, чтобы напечатать трассировку), но не подходит для файла размером 15 МБ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...