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

时间:2016-06-09 12:33:19

标签: python python-3.x

我试图禁用stdin缓冲,以便读取ANSI代码\033[6n的响应(应报告光标位置)。

我按照回答Setting smaller buffer size for sys.stdin?中的建议尝试了stdin_ub = os.fdopen(stdin.fileno(), 'rb', buffering=0),但程序仍然在第一次尝试阅读的第ch = stdin_ub.read(1)行被阻止。当返回键入终端时,它会解锁,这表明stdin仍然是行缓冲的。

供参考,这里是完整的代码:

def getpos():
    stdin_ub = os.fdopen(sys.stdin.fileno(), 'rb', buffering=0)
    sys.stdout.write('\033[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

2 个答案:

答案 0 :(得分: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]))

答案 1 :(得分:0)

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

curses模块将提供一种几乎可移植的方式来控制线路规则,不幸的是它在Windows系统上不起作用。

如果可以使用它,则必须使用

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

进入原始模式(通常应该设置回声)

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

恢复正常更多