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



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



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

612   15  

15 ответов:

linecache:

The 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Comments

    Ничего не найдено.