Как перейти к определенной строке в огромном текстовом файле?

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

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) строками неизвестной, но другой длины, и вам нужно перейти к определенной строке, номер которой я знаю заранее? Мне плохо, обрабатывая их один за другим, когда я знаю, что могу игнорировать хотя бы первую половину файла. Ищите более элегантное решение, если оно есть.

Ответ 1

linecache:

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

Ответ 2

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

# 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])

Ответ 3

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

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

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

Ответ 4

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

Ответ 5

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

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)

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

Ответ 6

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

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

Ответ 7

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

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

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

Ответ 8

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

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

Ответ 9

Я удивлен, что никто не упоминает islice

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

Ответ 10

Соблюдаете ли строки какие-либо данные индекса? Если содержание каждой строки было чем-то вроде "<line index>:Data", то подход seek() мог использоваться для выполнения двоичного поиска через файл, даже если значение Data является переменной. Вы искали бы среднюю точку файла, читали строку, проверяли, превышает ли ее индекс или меньше, чем тот, который вам нужен, и т.д.

В противном случае лучшее, что вы можете сделать, это просто readlines(). Если вы не хотите читать все 15 МБ, вы можете использовать аргумент sizehint, чтобы по крайней мере заменить много readline() меньшим количеством вызовов на readlines().

Ответ 11

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

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

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

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 - вы получите целевую строку.

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

Ответ 12

Здесь приведен пример использования "readlines (sizehint)" для чтения фрагмента строк за раз. DNS указал на это решение. Я написал этот пример, потому что другие примеры здесь ориентированы на одну строку.

def getlineno(filename, lineno):
    if lineno < 1:
        raise TypeError("First line is line 1")
    f = open(filename)
    lines_read = 0
    while 1:
        lines = f.readlines(100000)
        if not lines:
            return None
        if lines_read + len(lines) >= lineno:
            return lines[lineno-lines_read-1]
        lines_read += len(lines)

print getlineno("nci_09425001_09450000.smi", 12000)

Ответ 13

Вы можете использовать mmap, чтобы найти смещение линий. Похоже, что MMAP является самым быстрым способом обработки файла

Пример:

with open('input_file', "r+b") as f:
    mapped = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
    i = 1
    for line in iter(mapped.readline, ""):
        if i == Line_I_want_to_jump:
            offsets = mapped.tell()
        i+=1

затем используйте f.seek(смещения), чтобы перейти к нужной строке

Ответ 14

Если вы имеете дело с текстовым файлом и на основе Linux-системы, вы можете использовать команды linux.
Для меня это сработало хорошо!

import commands

def read_line(path, line=1):
    return commands.getoutput('head -%s %s | tail -1' % (line, path))

line_to_jump = 141978
read_line("path_to_large_text_file", line_to_jump)

Ответ 15

Может использовать эту функцию для возврата строки n:

def skipton(infile, n):
    with open(infile,'r') as fi:
        for i in range(n-1):
            fi.next()
        return fi.next()