在纯 Python 中读取 backspace、DEL 和来自 stdin 的朋友



如果有人用谷歌搜索退格 python stdin 左右,就会有很多 SO 结果,但没有一个能解决我的问题。它们都是关于退格字符是什么,而不是如何获得它


这是一个从stdin读取单个按键的函数,礼貌 https://stackoverflow.com/a/6599441/4532996:

def read_single_keypress():
    import termios, fcntl, sys, os
    fd = sys.stdin.fileno()
    # save old state
    flags_save = fcntl.fcntl(fd, fcntl.F_GETFL)
    attrs_save = termios.tcgetattr(fd)
    # make raw - the way to do this comes from the termios(3) man page.
    attrs = list(attrs_save) # copy the stored version to update
    # iflag
    attrs[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK
                  | termios.ISTRIP | termios.INLCR | termios. IGNCR
                  | termios.ICRNL | termios.IXON )
    # oflag
    attrs[1] &= ~termios.OPOST
    # cflag
    attrs[2] &= ~(termios.CSIZE | termios. PARENB)
    attrs[2] |= termios.CS8
    # lflag
    attrs[3] &= ~(termios.ECHONL | termios.ECHO | termios.ICANON
                  | termios.ISIG | termios.IEXTEN)
    termios.tcsetattr(fd, termios.TCSANOW, attrs)
    # turn off non-blocking
    fcntl.fcntl(fd, fcntl.F_SETFL, flags_save & ~os.O_NONBLOCK)
    # read a single keystroke
    try:
        ret = sys.stdin.read(1) # returns a single character
    except KeyboardInterrupt:
        ret = 0
    finally:
        # restore old state
        termios.tcsetattr(fd, termios.TCSAFLUSH, attrs_save)
        fcntl.fcntl(fd, fcntl.F_SETFL, flags_save)
    return ret

虽然很笨拙,但它似乎是跨平台的。

实现它是我的模块中的实用程序函数:

def until(char) -> str:
    """get chars of stdin until char is read"""
    import sys
    y = ""
    sys.stdout.flush()
    while True:
        i = read_single_keypress()
        _ = sys.stdout.write(i)
        sys.stdout.flush()
        if i == char or i == 0:
            break
        y += i
    return y

这真的很有效,除了按退格键什么都不做,而且你不能移动光标(import readline; input()允许你这样做(至少在使用 GNU Readline 构建的 Python 上((。

我知道实现这两个方面的最佳方法可能是curses。我也明白curses破坏了这个模块的标准库性和跨平台性。

我正在寻找一种以捕获退格的方式读取stdin的方法,并且有一个特殊的奖励,DEL 最好还有箭头键。

该模块同时针对 Python 2 和 3,但我对仅针对 Python 3 的解决方案没问题,因为人们真的需要停止使用 2。


如果你认为我因为想在没有curses的情况下这样做而发疯,那么,这就是重点。

考虑使用来自 ActiveState 的这个配方,这也是这个 SO 答案:

class _Getch:
    """Gets a single character from standard input.  Does not echo to the
screen."""
    def __init__(self):
        try:
            self.impl = _GetchWindows()
        except ImportError:
            self.impl = _GetchUnix()
    def __call__(self): return self.impl()
class _GetchUnix:
    def __init__(self):
        import tty, sys
    def __call__(self):
        import sys, tty, termios
        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

class _GetchWindows:
    def __init__(self):
        import msvcrt
    def __call__(self):
        import msvcrt
        return msvcrt.getch()

getch = _Getch()
# then call getch() to get the input...

原始模式下的终端驱动程序确实允许读取 BS ^H 和 DEL 字符。 箭头键往往是 ESC 序列,而不是一个字节,除非您拥有 80 年代真正的 Wyse 终端之类的东西。 像箭头键这样的 GNU readline 功能不会在原始模式下工作,也就是说不要做任何字符处理。

开发功能键映射器对于支持ANSI/DEC VT100样式终端对于编写的应用程序是必要的,该应用程序假设1键= 1输入字节,有效地使用Wyse终端控制字符作为内部命令。

同样,在通过写入输出时,除非读取的输入字节被转换,也许通过管道转换为 od(1(,否则您将如何看到非打印字符?

curses 库更多的是关于屏幕绘制和高效更新,它使用原始模式和 getch(( 例程来允许菜单选项等,而无需点击返回。 如果您只是读取键盘输入,则没有必要。

相关内容

  • 没有找到相关文章

最新更新