简体   繁体   English

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

[英]Disable buffering of sys.stdin in Python 3

I'm trying to disable stdin buffering, in order to read the response of ANSI code \\033[6n (which should report the cursor position). 我正在尝试禁用stdin缓冲,以便读取ANSI代码\\033[6n (应报告光标位置)的响应。

I tried stdin_ub = os.fdopen(stdin.fileno(), 'rb', buffering=0) as suggested in answer Setting smaller buffer size for sys.stdin? 我按照回答为sys.stdin设置较小的缓冲区大小的建议尝试了stdin_ub = os.fdopen(stdin.fileno(), 'rb', buffering=0) , but still the program is blocked at line ch = stdin_ub.read(1) of the first attempt to read. ,但程序仍然在第一次尝试读取的行ch = stdin_ub.read(1)时被阻止。 It unblocks when return is typed into the terminal, which suggests the stdin is still line buffered. 当返回键入终端时,它会解锁,这表明stdin仍然是行缓冲的。

For reference, here's the complete code: 供参考,这是完整的代码:

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

I'm using python 3.5.1 我正在使用python 3.5.1

The trick is to use tty.setcbreak(sys.stdin.fileno(), termios.TCSANOW) and before that store the terminal attributes via termios.getattr in variable to restore the default behavior. 诀窍是使用tty.setcbreak(sys.stdin.fileno(), termios.TCSANOW) ,然后通过termios.getattr在变量中存储终端属性以恢复默认行为。 With cbreak set, sys.stdin.read(1) is unbuffered. 使用cbreak set, sys.stdin.read(1)是无缓冲的。 This also suppress the ansi controll code response from the terminal. 这也抑制了来自终端的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]))

Unfortunately, there is no portable way to do that. 不幸的是,没有可移植的方法来做到这一点。 The underlying IO system is line buffered when reading from keyboard on common OS, for example Windows and Unix families. 当从普通操作系统上的键盘读取时,底层IO系统是行缓冲的,例如Windows和Unix系列。

The curses module would offer an almost portable way to control the line discipline , unfortunately it does not work on windows systems. curses模块将提供一种几乎可移植的方式来控制线路规则 ,遗憾的是它在Windows系统上不起作用。

If you can use it, you will have to use 如果你可以使用它,你将不得不使用

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

to enter raw mode (generally echo should be set off) 进入原始模式(通常应该设置回声)

and

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

to return to normal cooked more 要恢复正常多了

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM