Python,"filtered"行编辑,按字符读取标准,没有回声



我需要一个像raw_input()那样将输入读入缓冲区的函数,但不是回退输入并阻塞直到返回整行,它应该抑制echo并在每次缓冲区更改时调用回调。

我说"缓冲区更改"而不是"字符被读取",因为作为raw_input(),我希望它能够意识到特殊键。例如,退格应该可以工作。

如果我想,例如,使用回调来模拟输入的大写回显,代码看起来像这样:

def callback(text):
    print 'r' + text.upper()
read_input(callback)

我怎样才能做到这一点?

注意:我一直在尝试使用readlinecurses来满足我的目的,但是两个Python绑定都是不完整的。curses不能在不清空整个屏幕的情况下启动,readline在任何输入开始之前提供一个单一的钩子。

好吧,我是手工编写代码的。我留个解释,以备将来参考。

<标题>要求
import sys, tty, termios, codecs, unicodedata
from contextlib import contextmanager

禁用行缓冲

简单读取stdin时出现的第一个问题是行缓冲。我们希望单个字符不需要换行符就能到达程序,这不是终端的默认操作方式。

为此,我编写了一个上下文管理器来处理tty配置:
@contextmanager
def cbreak():
    old_attrs = termios.tcgetattr(sys.stdin)
    tty.setcbreak(sys.stdin)
    try:
        yield
    finally:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_attrs)

此管理器启用以下习惯用法:

with cbreak():
    single_char_no_newline = sys.stdin.read(1)

完成后执行清理非常重要,否则终端可能需要reset .

<标题>解码stdin h1> 读取stdin的第二个问题是编码。非ascii unicode字符将一个字节一个字节地到达我们,这是完全不希望的。

为了正确解码stdin,我编写了一个可以迭代unicode字符的生成器:

def uinput():
    reader = codecs.getreader(sys.stdin.encoding)(sys.stdin)
    with cbreak():
        while True:
            yield reader.read(1)

这可能在管道上失败。我不确定。然而,对于我的用例,它选择正确的编码并生成一个字符流。

处理特殊字符

首先,我们应该能够区分可打印字符和控制字符:

def is_printable(c):
    return not unicodedata.category(c).startswith('C')

除了可打印的,现在,我只想处理←退格CtrlD序列:

def is_backspace(c):
    return c in ('x08','x7F')
def is_interrupt(c):
    return c == 'x04'

组合:xinput()

现在一切就绪。我想要的函数的原始合约是读取输入,处理特殊字符,调用回调。实现仅仅反映了这一点:

def xinput(callback):
    text = ''
    for c in uinput():
        if   is_printable(c): text += c
        elif is_backspace(c): text = text[:-1]
        elif is_interrupt(c): break
        callback(text)
    return text

试用

def test(text):
    print 'Buffer now holds', text
xinput(test)

运行并输入Hellx←backspaceo World显示:

Buffer now holds H
Buffer now holds He
Buffer now holds Hel
Buffer now holds Hell
Buffer now holds Hellx
Buffer now holds Hell
Buffer now holds Hello
Buffer now holds Hello 
Buffer now holds Hello w
Buffer now holds Hello wo
Buffer now holds Hello wor
Buffer now holds Hello worl
Buffer now holds Hello world

最新更新