在 Python 3 中禁用 sys.stdin 的缓冲



我正在尝试禁用标准缓冲,以便读取ANSI代码33[6n的响应(应报告光标位置)。

我按照答案中的建议尝试stdin_ub = os.fdopen(stdin.fileno(), 'rb', buffering=0)为 sys.stdin?设置较小的缓冲区大小,但该程序仍然在第一次尝试读取的第 ch = stdin_ub.read(1) 行被阻止。当在终端中键入 return 时,它会解锁阻塞,这表明 stdin 仍然处于行缓冲状态。

作为参考,下面是完整的代码:

def getpos():
    stdin_ub = os.fdopen(sys.stdin.fileno(), 'rb', buffering=0)
    sys.stdout.write('33[6n')
    sys.stdout.flush()
    ch, k, field = None, -1, [b'', b'']
    while True:
        #print('reading wait...')
        ch = stdin_ub.read(1)
        #print('reading OK')
        if ch == b'[': k = 0
        elif ch == b';': k = 1
        elif ch == b'R': break
        elif k >= 0: field[k] += ch
    try:
        return tuple(map(int, field))
    except:
        pass

我正在使用python 3.5.1

诀窍

是使用tty.setcbreak(sys.stdin.fileno(), termios.TCSANOW)和之前通过变量中的termios.getattr存储终端属性来恢复默认行为。设置cbreak后,sys.stdin.read(1)是无缓冲的。这也抑制了来自终端的 ansi 控制代码响应。

def getpos():
    buf = ""
    stdin = sys.stdin.fileno()
    tattr = termios.tcgetattr(stdin)
    try:
        tty.setcbreak(stdin, termios.TCSANOW)
        sys.stdout.write("x1b[6n")
        sys.stdout.flush()
        while True:
            buf += sys.stdin.read(1)
            if buf[-1] == "R":
                break
    finally:
        termios.tcsetattr(stdin, termios.TCSANOW, tattr)
    # reading the actual values, but what if a keystroke appears while reading
    # from stdin? As dirty work around, getpos() returns if this fails: None
    try:
        matches = re.match(r"^x1b[(d*);(d*)R", buf)
        groups = matches.groups()
    except AttributeError:
        return None
    return (int(groups[0]), int(groups[1]))

不幸的是,没有便携式方法可以做到这一点。在常见操作系统(例如 Windows 和 Unix 系列)上从键盘读取时,底层 IO 系统是行缓冲的。

curses 模块将提供一种几乎便携的方式来控制线路纪律,不幸的是它不适用于 Windows 系统。

如果你可以使用它,你将不得不使用

curses.noecho()
curses.raw()   # or curses.cbreak()

进入原始模式(一般应关闭回声)

curses.echo()
curses.noraw()   # resp. curses.nocbreak()

恢复正常煮更多

最新更新