Как перейти к определенной строке в огромном текстовом файле?
существуют ли какие-либо альтернативы приведенному ниже коду:
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 ответов:
вы не можете прыгнуть вперед, не прочитав файл хотя бы один раз, так как вы не знаете, где находятся разрывы строк. Вы могли бы сделать что-то вроде:
# 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 linet. 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