3 に答える
例外は Python からではなく、パイプのシークを許可しないオペレーティング システムから発生します。(通常のパイプからの出力をリダイレクトすると、標準入力であってもシークできます。)これが、クラスが同じであっても、あるケースではエラーが発生し、別のケースではエラーが発生しない理由です。
先読みのための従来の Python 2 ソリューションは、先読みを実装する独自のストリーム実装でストリームをラップすることです。
class Peeker(object):
def __init__(self, fileobj):
self.fileobj = fileobj
self.buf = cStringIO.StringIO()
def _append_to_buf(self, contents):
oldpos = self.buf.tell()
self.buf.seek(0, os.SEEK_END)
self.buf.write(contents)
self.buf.seek(oldpos)
def peek(self, size):
contents = self.fileobj.read(size)
self._append_to_buf(contents)
return contents
def read(self, size=None):
if size is None:
return self.buf.read() + self.fileobj.read()
contents = self.buf.read(size)
if len(contents) < size:
contents += self.fileobj.read(size - len(contents))
return contents
def readline(self):
line = self.buf.readline()
if not line.endswith('\n'):
line += self.fileobj.readline()
return line
sys.stdin = Peeker(sys.stdin)
sys.stdin
デコードされていないストリームをピークしながら完全にサポートする Python 3 では、複雑です。stdin.buffer
上記のようにラップし、ピーク可能なTextIOWrapper
ストリームに対して新しいインスタンスを作成し、それをTextIOWrapper
としてインストールしsys.stdin
ます。
ただし、 をのぞくだけでよいのでsys.stdin.buffer
、上記のコードは と に変更した後でも問題なく動作cStringIO.StringIO
します。io.BytesIO
'\n'
b'\n'
user4815162342 のソリューションは非常に便利ですが、io.BufferedReader peek メソッドの現在の動作とは異なるという問題があるようです。
組み込みメソッドは、順次 peek() 呼び出しに対して同じデータを (現在の読み取り位置から開始して) 返します。
user4815162342 のソリューションは、シーケンシャル ピーク コールごとにデータのシーケンシャル チャンクを返します。これは、ユーザーが同じデータを複数回使用したい場合、peek を再度ラップして出力を連結する必要があることを意味します。
組み込みの動作を返すための修正は次のとおりです。
def _buffered(self):
oldpos = self.buf.tell()
data = self.buf.read()
self.buf.seek(oldpos)
return data
def peek(self, size):
buf = self._buffered()[:size]
if len(buf) < size:
contents = self.fileobj.read(size - len(buf))
self._append_to_buf(contents)
return self._buffered()
return buf
適用できる他の最適化があります。たとえば、バッファを使い果たす読み取り呼び出しで以前にバッファリングされたデータを削除します。現在の実装では、ピークされたデータはバッファに残りますが、そのデータにはアクセスできません。