2014-11-02 4 views
2

Я хотел бы прочитать файл по строкам, за исключением последних N строк. Как я могу узнать, где остановиться, не дойдя до конца файла и отслеживая/отбрасывая последние N строк в Python? Является ли запрос # lines = X и цикл (X-N) хорошим способом?Простой способ НЕ читать последние N строк файла в Python

Что такое самый простой/самый Pythonic способ сделать это?

+5

В общем случае, если строки могут иметь переменную длину, есть * no way *, Pythonic или иначе, зная, сколько строк находится в части файла, который вы не читали. –

+0

вы можете прочитать файл с помощью 'readlines', а затем применить' len', чтобы получить общее количество строк в файле, теперь вы можете сделать – Hackaholic

+1

@Hackaholic, вы только что прочитали эти строки, хотя ... Вместо того, чтобы len вы можете просто нарезать его [: -N] ... который «отбрасывает последние N строк» ​​... –

ответ

2

три различных решения:

1) Быстрый и грязный см ответ Джона:

with open(file_name) as fid: 
    lines = fid.readlines() 
for line in lines[:-n_skip]: 
    do_something_with(line) 

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

2) два прохода

процесса файл дважды, один раз, чтобы подсчитать число линий n_lines, а во втором процессе прохода только первые n_lines - n_skip линии:

# first pass to count 
with open(file_name) as fid: 
    n_lines = sum(1 for line in fid) 

# second pass to actually do something 
with open(file_name) as fid: 
    for i_line in xrange(n_lines - n_skip): # does nothing if n_lines <= n_skip 
     line = fid.readline() 
     do_something_with(line) 

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

3) Используйте буфер, похожий на решение Сержа

В случае, если вы хотите перебрать файл только один раз, вы знаете, только убедитесь, что вы можете обработать линию i, если вы знаете, что линия i + n_skip существует , Это означает, что вы должны сначала сохранить n_skip строк во временном буфере. Один из способов сделать это, чтобы реализовать какой-то буфер FIFO (например, с помощью функции генератора, который реализует кольцевой буфер):

def fifo(it, n): 
    buffer = [None] * n # preallocate buffer 
    i = 0 
    full = False 
    for item in it: # leaves last n items in buffer when iterator is exhausted 
     if full: 
      yield buffer[i] # yield old item before storing new item 
     buffer[i] = item 
     i = (i + 1) % n 
     if i == 0: # wrapped around at least once 
      full = True 

Быстрый тест с диапазоном чисел:

In [12]: for i in fifo(range(20), 5): 
    ...:  print i, 
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 

То, как вы бы использовать этот файл:

with open(file_name) as fid: 
    for line in fifo(fid, n_skip): 
     do_something_with(line) 

Заметим, что для этого требуется достаточно памяти для временного хранения n_skip линий, но это все равно лучше, чем читать все строки в памятке как в первом решении.

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

1

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

  1. Когда вы пишете файл, сохраните положение последних X строк. Прекратите чтение при достижении этой позиции.
  2. Сохраните позиции начала строки где-нибудь, это позволяет добавлять к файлу.
  3. Вы знаете размер линий.
    1. Каждая строка может иметь такой же размер, и вы вычислить его из размера файла
    2. Каждая строка имеет по крайней мере один символ, так что вам не нужно читать последние X символов.
1

Учитывая мы знаем, что файл должен быть прочитан до конца, чтобы определить, сколько строк есть, вот моя попытка на «простой/наиболее вещем пути» чтение до последних n линий:

with open(foo, 'r') as f: 
    lines = f.readlines()[:-n] 
+2

Проще может быть: 'lines = f.readlines() [: - n]', ciao от – gboffi

+0

Конечно, я не знаю, почему я так не писал, изначально устал, я думаю :) –

+1

В Лос-Анджелесе, а? В Италии мы предпочитаем «Спокойной ночи и мечты о золоте!». – gboffi

2

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

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

with open(file) as fd: 
    lines = [] 
    try: 
     for i in range(N): 
      lines.append(next(fd)) 

     i = 0 
     for line in fd: 
      # process lines[i] 
      print (lines[i].rstrip()) 
      lines[i] = line 
      i = (i + 1) % N 
    except StopIteration: 
     print "less than %d lines" % (N,) 
Смежные вопросы