Итератор файла Python над двоичным файлом с более новой идиомой - PullRequest
23 голосов
/ 31 декабря 2010

В Python для двоичного файла я могу написать это:

buf_size=1024*64           # this is an important size...
with open(file, "rb") as f:
   while True:
      data=f.read(buf_size)
      if not data: break
      # deal with the data....

С текстовым файлом, который я хочу читать построчно, я могу написать это:

with open(file, "r") as file:
   for line in file:
       # deal with each line....

Что является сокращением для:

with open(file, "r") as file:
   for line in iter(file.readline, ""):
       # deal with each line....

Эта идиома описана в PEP 234 , но мне не удалось найти подобную идиому для двоичных файлов.

Я пробовал это:

>>> with open('dups.txt','rb') as f:
...    for chunk in iter(f.read,''):
...       i+=1

>>> i
1                # 30 MB file, i==1 means read in one go...

Я пытался поставить iter(f.read(buf_size),''), но это синтаксическая ошибка из-за паренов после вызова в iter ().

Я знаю, что мог бы написать функцию, но есть ли способ с идиомой по умолчанию for chunk in file:, где я могу использовать размер буфера по сравнению со строкой?

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

Ответы [ 2 ]

33 голосов
/ 31 декабря 2010

Попробуйте:

>>> with open('dups.txt','rb') as f:
...    for chunk in iter((lambda:f.read(how_many_bytes_you_want_each_time)),''):
...       i+=1

iter нужна функция с нулевыми аргументами.

  • обычный f.read будет читать весь файл, так как параметр size отсутствует;
  • f.read(1024) означает вызов функции и передачу ее возвращаемого значения (данных, загруженных из файла) в iter, поэтому iter вообще не получает функцию;
  • (lambda:f.read(1234)) - это функция, которая принимает нулевые аргументы (ничего между lambda и :) и вызывает f.read(1234).

Существует эквивалентность между:

somefunction = (lambda:f.read(how_many_bytes_you_want_each_time))

и

def somefunction(): return f.read(how_many_bytes_you_want_each_time)

и имея один из них перед вашим кодом, вы можете просто написать: iter(somefunction, '').

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

22 голосов
/ 31 декабря 2010

Я не знаю ни одного встроенного способа сделать это, но функцию-оболочку достаточно просто написать:

def read_in_chunks(infile, chunk_size=1024*64):
    while True:
        chunk = infile.read(chunk_size)
        if chunk:
            yield chunk
        else:
            # The chunk was empty, which means we're at the end
            # of the file
            return

Затем в интерактивном режиме:

>>> from chunks import read_in_chunks
>>> infile = open('quicklisp.lisp')
>>> for chunk in read_in_chunks(infile):
...     print chunk
... 
<contents of quicklisp.lisp in chunks>

Конечно, вы можете легко адаптировать это для использования с блоком:

with open('quicklisp.lisp') as infile:
    for chunk in read_in_chunks(infile):
        print chunk

И вы можете исключить оператор if следующим образом.

def read_in_chunks(infile, chunk_size=1024*64):
    chunk = infile.read(chunk_size)
    while chunk:
        yield chunk
        chunk = infile.read(chunk_size)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...