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

在Python 3中禁用sys.stdin的缓冲,python,python-3.x,Python,Python 3.x,我试图禁用stdin缓冲,以便读取ANSI代码\033[6n(应该报告光标位置)的响应 我按照回答中的建议尝试了stdin\u ub=os.fdopen(stdin.fileno(),'rb',buffering=0),但程序仍然在第一次尝试读取的第ch=stdin\u ub.read(1)行被阻塞。当在终端中键入return时,它会解除阻塞,这表明stdin仍然是行缓冲的 以下是完整的代码供参考: def getpos(): stdin_ub = os.fdopen(sys.stdin

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

我按照回答中的建议尝试了
stdin\u ub=os.fdopen(stdin.fileno(),'rb',buffering=0)
,但程序仍然在第一次尝试读取的第
ch=stdin\u ub.read(1)
行被阻塞。当在终端中键入return时,它会解除阻塞,这表明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

不幸的是,没有可移植的方法来实现这一点。在普通操作系统(例如Windows和Unix系列)上,从键盘读取时,底层IO系统是行缓冲的

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

如果你能使用它,你就必须使用它

curses.noecho()
curses.raw()   # or curses.cbreak()
进入原始模式(通常应设置回波)


不幸的是,要恢复正常,没有可移植的方法。在普通操作系统(例如Windows和Unix系列)上,从键盘读取时,底层IO系统是行缓冲的

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

如果你能使用它,你就必须使用它

curses.noecho()
curses.raw()   # or curses.cbreak()
进入原始模式(通常应设置回波)


要恢复到正常状态,技巧是使用
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]))

诀窍是使用
tty.setcbreak(sys.stdin.fileno(),termios.TCSANOW)
并在此之前通过变量
termios.getattr
存储终端属性,以恢复默认行为。使用
cbreak
set,
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]))

os.fdopen(sys.stdin.fileno(),'rb'中的
'rb'
中删除
b
?@MoonCheesez你有没有读到我在使用python 3?:p只是在冒险猜测。
'b'
通常意味着缓冲区,因此删除它可能会有所帮助。@MoonCheesez猜测不起作用。需要5秒钟来验证你的建议是错误的,因为它会产生错误,那么你为什么一直在浪费时间进行随机猜测?@MoonCheesez另外,“
b
通常意味着缓冲区”是错误的。如果您不确定您的声明,请不要评论/回答。从
os.fdopen(sys.stdin.fileno(),'rb'中的
'rb'
中删除
b
?@MoonCheesez你有没有读到我在使用python 3?:p只是在冒险猜测。
'b'
通常意味着缓冲区,因此删除它可能会有所帮助。@MoonCheesez猜测不起作用。需要5秒钟来验证你的建议是错误的,因为它会产生错误,那么你为什么一直在浪费时间进行随机猜测?@MoonCheesez另外,“
b
通常表示缓冲区”是错误的。如果您不确定您的声明,请不要评论/回答。使用curses的问题是它需要您运行
curses.initscr()
,这会导致清除终端不必要的副作用使用curses的问题是需要运行
curses.initscr()
,这会导致清除终端不必要的副作用