如何在不等待输入的情况下检查潜在的空stdin?

时间:2014-10-08 18:13:26

标签: python io buffer stdin stdio

我试图从键盘输入读取而不等待输入。目的是在无限的"循环ala而True:。

到目前为止,我一直试图操纵readchar库https://pypi.python.org/pypi/readchar/0.6,但没有运气。虽然它不等待Enter,但它仍然等待一些输入。我不希望它等待输入,而只是检查并返回""如果没有输入,则为一些占位符。

以下是我一直在使用的内容:

def readchar():
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
    tty.setraw(sys.stdin.fileno())
    ch = sys.stdin.read(1)
finally:
    termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch

def main():
    while True:
        current = readchar()
        if current == "some letter":
            print("things happen")

1 个答案:

答案 0 :(得分:3)

最终构成Python文件对象的POSIX I / O函数有两种不同的模式,阻塞和非阻塞,由一个名为O_NONBLOCK的标志控制。特别是,read函数说:

  

尝试读取文件时......当前没有数据...如果设置了O_NONBLOCK read()将返回-1并设置 errno [EAGAIN]

在Python中,os模块中提供了此标志。


sys.stdin已经打开,因此您无法将O_NONBLOCK传递给os.open函数,所以......你做了什么?好吧,你实际上可能想要打开/dev/tty;这取决于你实际在做什么。在那种情况下,答案是显而易见的。但是我们假设你没有。因此,您想要更改已打开文件的标志。这正是fcntl的用途。您可以使用F_GETFL操作来读取当前标志,或者使用O_NONBLOCK的位和F_SETFL结果。当然,如果你想要恢复,你可以记住当前的标志。

在Python中,fcntl模块中提供了sys.stdin函数和操作常量。


最后一个问题:sys.stdin.read()不是原始文件对象,它是fcntl,它在TextIOWrapper之上进行Unicode解码,它本身在BufferedReader之上添加缓冲。 {3}}。因此,read不直接调用POSIX sys.stdin.buffer.raw函数。为此,您需要使用bytes。如果你想在原始输入和正常输入之间来回切换,你可能还需要进行大量的小心刷新。 (注意,这意味着你放弃了Unicode,而是获得了一个单字节FileIO.read对象,例如,可能是UTF-8字符的一半或终端转义字符的四分之一。希望你'期待那样。)


现在,RawIOBase什么都没有可用时返回?嗯,这是None的实现,FileIO说:

  

如果返回0个字节,且size不为0,则表示文件结束。如果对象处于非阻塞模式且没有可用字节,则返回None

换句话说,如果没有可用内容,您将获得b'',对于EOF,您将获得bytes,或者为其他任何内容获得单字节old_settings = termios.tcgetattr(fd) old_flags = fcntl.fcntl(fd, fcntl.F_GETFL) try: tty.setraw(fd) fcntl.fcntl(fd, fcntl.F_SETFL, old_flags | os.O_NONBLOCK) return sys.stdin.buffer.raw.read(1) finally: fcntl.fcntl(fd, fcntl.F_SETFL, old_flags) termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)


所以,把它们放在一起:

select

最后一件事:有没有办法告诉输入是否准备就绪而不阅读它?是的,但不是便携式的。根据您的平台,pollepollkqueue和/或read(0)可能可用,并且可能适用于常规文件。 b''可以保证None而不是read。等等。您可以阅读本地手册页。但更简单的解决方案与C的stdio相同:添加1字节最大缓冲区,并使用它来实现自己的peekreadunread和{{1}包装。