我想读取字节。 sys.stdin
在textmode中打开,但它有一个可用于读取字节的缓冲区:sys.stdin.buffer
。
我的问题是,当我将数据传输到python时,如果我想要预读,我似乎只有2个选项,否则我得到io.UnsupportedOperation: File or stream is not seekable.
从sys.stdin
读取缓冲文本,将该文本解码为字节,然后回头
(sys.stdin.read(1).decode(); sys.stdin.seek(-1, io.SEEK_CUR)
。
由于输入流中的不可编码字节而无法接受。
使用peek
从stdin的缓冲区中获取一些字节,将其切换为适当的数字,然后祈祷,因为peek
并不保证任何内容:它可能会给出少于或多于你要求......
(sys.stdin.buffer.peek(1)[:1]
)
peek实际上是未记录的,并且为您提供了大量字节,您必须对性能密切切片。
顺便说一句。该错误实际上仅适用于管道:对于./myscript.py <somefile
,sys.stdin.buffer
支持搜索。但sys.stdin
始终是对象的相同层次结构:
$ cat testio.py
#!/usr/bin/env python3
from sys import stdin
print(stdin)
print(stdin.buffer)
print(stdin.buffer.raw)"
$ ./testio.py
<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>
<_io.BufferedReader name='<stdin>'>
<_io.FileIO name='<stdin>' mode='rb'>
$ ./testio.py <somefile
[the same as above]
$ echo hi | ./testio.py
[the same as above]
将字节流包装到随机访问缓冲区中的一些初步想法失败,并出现与上述相同的错误:BufferedRandom(sys.stdin.buffer).seek(0)
⇒io.UnsupportedOperation…
最后,为了您的方便我现在:
IOBase
├RawIOBase
│└FileIO
├BufferedIOBase (buffers a RawIOBase)
│├BufferedWriter┐
│├BufferedReader│
││ └─────┴BufferedRWPair
│├BufferedRandom (implements seeking)
│└BytesIO (wraps a bytes)
└TextIOBase
├TextIOWrapper (wraps a BufferedIOBase)
└TextIO (wraps a str)
如果您忘记了这个问题:如何在没有de /编码任何内容的情况下从stdin获取下一个字节,并且不推进流的光标?
答案 0 :(得分:6)
异常不是来自Python,而是来自操作系统,它不允许在管道上搜索。 (如果你从常规管道重定向输出,它可以被搜索,即使它是标准输入。)这就是你在一种情况下而不是在另一种情况下得到错误的原因,即使这些类是相同的。
用于readahead的经典Python 2解决方案是将流包装在您自己的实现readahead的流实现中:
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'
更改为{{1}后,上述代码将正常运行}。
答案 1 :(得分:3)
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
可以应用其他优化措施,例如在耗尽缓冲区的读取调用时移除先前缓冲的数据。当前实现在缓冲区中留下任何偷看的数据,但该数据无法访问。
答案 2 :(得分:0)
尝试一下:
import sys
ssb = sys.stdin.buffer.read(1)
if ssb == b'h':
print(ssb+sys.stdin.buffer.read())
回显字符串:
a@fuhq:~$ echo 'hi' | python3 buf_test.py
b'hi\n'
重定向文件:
a@fuhq:~$ cat hi.text
hi
a@fuhq:~$ python3 buf_test.py < hi.text
b'hi\n'