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

существуют ли альтернативы приведенному ниже коду:

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 ответов


linecache:

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


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

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

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

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

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


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


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

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)

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


Я удивлен, что никто не упомянул 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

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

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


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

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

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


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

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

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

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

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

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

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

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


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

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


вот пример использования "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)

вы можете использовать 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.ищите (смещения), чтобы перейти к нужной вам строке


Если вы имеете дело с текстовый файл & основе система 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)

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

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