2010-06-10 2 views
0

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

  1. for i in range(n): 
        fname.readline() 
    k=readline() 
    print k 
    
  2. i=0 
    for line in fname: 
        dictionary[i]=line 
        i=i+1 
    

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

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

Поблагодарив U

+1

Вам нужна только n-я строка файла, или вам нужны все строки до n? – danben

+0

нужна только n-я строка – kaushik

ответ

5

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

Если файлы являются огромными, я рекомендую что-то вроде (предупреждение, непроверенный код):

def readlinenum(filepath, n, BUFSIZ=65536): 
    bufs = [None] * 2 
    previous_lines = lines_so_far = 0 
    with open(filepath, 'b') as f 
    while True: 
     bufs[0] = f.read(BUFSIZ) 
     if not bufs[0]: 
     raise ValueError('File %s has only %d lines, not %d', 
         filepath, lines_so_far, n) 
     lines_this_block = bufs[0].count('\n') 
     updated_lines_count = lines_so_far + lines_this_block 
     if n < updated_lines_count: 
      break 
     previous_lines = lines_so_far 
     lines_so_far = updated_lines_count 
     bufs[1] = bufs[0] 
    if n == lines_so_far: 
     # line split between blocks 
     buf = bufs[1] + bufs[0] 
     delta = n - previous_lines 
    else: # normal case 
     buf = bufs[0] 
     delta = n = lines_so_far 
    f = cStringIO.StringIO(buf) 
    for i, line in enumerate(f): 
     if i == delta: break 
    return line.rstrip() 

Общая идея заключается в том, чтобы прочитать в файле как в двоичной, в больших блоках (по крайней мере, большой как самая длинная строка) - обработка (в Windows) от двоичного до «текста» дорогостоящая на огромных файлах - и используйте быстрый метод строк на большинстве блоков .count. В конце мы можем провести синтаксический анализ строк на одном блоке (два не более в аномальном случае, когда искомая линия охватывает границы блоков).

Этот тип кода требует тщательного тестирования и проверки (который я не выполнял в этом случае), будучи склонным к ошибкам по очереди и другим границам, поэтому я бы рекомендовал его только для действительно огромных файлов - - те, которые по существу перегружают память при использовании linecache (который просто всасывает весь файл в память вместо работы блоками). Например, на типичной современной машине с 4 Гбайт байт ОЗУ я начинаю думать о таких методах для текстовых файлов, которые находятся над GB или двумя.

Редактировать: комментатор не считает, что двоичное чтение файла происходит намного быстрее, чем обработка, требуемая текстовым режимом (только для Windows). Чтобы показать, как это неправильно, давайте воспользуемся опцией 'U' («универсальная новая линия»), которая заставляет обработку на конец строки также выполняться на машинах Unix (поскольку у меня нет машины Windows для ее запуска;;). Используя обычный kjv.txt файл:

$ wc kjv.txt 
    114150 821108 4834378 kjv.txt 

(4.8 MB, 114 Klines) - около 1/1000-подобного размеров файлов я упоминал ранее:

$ python -mtimeit 'f=open("kjv.txt", "rb")' 'f.seek(0); f.read()' 
100 loops, best of 3: 13.9 msec per loop 
$ python -mtimeit 'f=open("kjv.txt", "rU")' 'f.seek(0); f.read()' 
10 loops, best of 3: 39.2 msec per loop 

т.е. примерно ровно в 3 раза для обработки линии (это на старом ноутбуке, но соотношение должно быть довольно повторяемым и в других местах).

Чтения петли на линии, конечно, еще медленнее:

$ python -mtimeit 'f=open("kjv.txt", "rU")' 'f.seek(0)' 'for x in f: pass' 
10 loops, best of 3: 54.6 msec per loop 

и использование readline как комментируемые упоминался (с менее эффективной буферизацией, чем непосредственно зацикливание на файл) является худшим:

$ python -mtimeit 'f=open("kjv.txt", "rU")' 'f.seek(0); x=1' 'while x: x=f.readline()' 
10 loops, best of 3: 81.1 msec per loop 

Если, как говорится в этом вопросе, есть 20 000 файлов для чтения (скажем, что они все мелкие, порядка kjv.txt), самый быстрый подход (чтение каждого файла в двоичном режиме одним глотком) должно занимать около 260 секунд, 4-5 минут es, в то время как самый медленный (на основе) должен занимать около 1600 секунд, почти полчаса - довольно значительная разница для многих, я бы сказал, наиболее реальных приложений.

+0

linecache помог мне, спасибо u – kaushik

+0

hmmm, это утверждение странно для меня - почему текстовые файлы будут медленными в Windows? у меня никогда не было таких проблем в c или pascal или python ... –

+0

@EnTerr, сколько опыта у вас есть обработка файлов 'rt' vs' rb' multi-GB? Чтение файла как 't' означает, что среда выполнения должна превратить каждый' \ r \ n' в '\ n' (и« сдвинуть »весь последующий текст в 1 байт в буфере) - над гигабайтами он уверен, (плюс, размещение 2-символьных последовательностей, таких как '\ r \ n', не так быстро, на уровне машины, как поиск одиночного символа' \ n'). У меня нет под рукой Windows-машины для подтверждения, но попробуйте сами: составьте текстовый файл размером 4 ГБ с 1000 объединенными копиями библейского текста короля Джеймса (каждый 4 МБ), читайте его как 'rb' vs' rt' много раз, измерения. –

2

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

Что касается ваших примеров:

  • xrange работает быстрее, чем range поскольку range должен создать список, в то время как xrange использует генератор
  • если вам нужно только линии n, почему вы хранения всех строк в словаре?
0

Кэшировать список смещений каждого символа конца строки в файле будет стоить много памяти, но кеширование примерно одной страницы памяти (как правило, 4 КБ) дает в основном такое же сокращение ввода-вывода, и стоимость сканирования пары KB от известного смещения незначительна. Итак, если ваша средняя длина строки составляет 40 символов, вам нужно только кэшировать список каждого 100-го конца строки в файле. Точно там, где вы рисуете линию, зависит от того, сколько у вас памяти и насколько быстро ваш ввод-вывод. Вы даже можете уйти с кешированием списка смещений каждого 1000-го символа конца строки без заметной разницы в производительности от индексации каждого из них.

Смежные вопросы