Использование io.BufferedReader для потока, полученного с помощью open()?

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

Но этот тестовый пример не выполняется:

AttributeError: объект 'file' не имеет атрибута '_checkReadable'

import sys
import io

srcfile = sys.argv[1]
with open(srcfile, 'rb') as f:
    fbuf = io.BufferedReader(f)
    print fbuf.read(20)

Что происходит и как я могу это исправить? Я думал, что BufferedReader предназначен для буферизации потока. Если да, то почему функция open() не возвращает что-то совместимое с ней?

Ответ 1

По внешнему виду вашего оператора print вы используете Python 2. В этой версии file не является допустимым аргументом для конструктора BufferedReader:

В Python 2.x это предлагается как альтернатива встроенному объекту file, но в Python 3.x это интерфейс по умолчанию для доступа к файлам и потокам. (1)

Вместо этого следует использовать io.open:

>>> f = io.open(".bashrc", "rb")

Если вы это сделаете, нет необходимости явно переносить его в BufferedReader, так как именно то, что io.open возвращает по умолчанию:

>>> type(f)
<type '_io.BufferedReader'>

Подробнее см. его документы; существует аргумент buffering, который управляет буферизацией.

В Python 3, open is io.open, поэтому две библиотеки ввода-вывода были объединены в один. Кажется, что io был добавлен в Python 2.6 в основном для передовой совместимости.

Ответ 2

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

import sys

srcfile = sys.argv[1]
with open(srcfile, 'rb', buffering=30) as f:
    print(f.peek(30))
    print(f.read(20))

Это BufferedReader:

>>> with open("test.txt", 'rb', buffering=30) as f:
...     type(f)
<class '_io.BufferedReader'>

Обратите внимание, что по умолчанию он буферизуется в 1 - строка буферизирована.

Ответ 3

В Python2, если вам нужно использовать объект file как возвращаемый open (или, например, предоставленный некоторыми модульными подпрограммами, которые вы не можете изменить), вы можете использовать дескриптор файла, полученный конструктором fileno() для io.FileIO, затем передать объект io.FileIO в конструктор io.BufferedReader.

Итак, образец кода можно переписать следующим образом:

import sys
import io

srcfile = sys.argv[1]
with open(srcfile, 'rb') as f:
    fio  = io.FileIO(f.fileno())
    fbuf = io.BufferedReader(fio)
    print fbuf.read(20)