Python,“过滤”行编辑,按字符读取标准输入,没有回声

Posted

技术标签:

【中文标题】Python,“过滤”行编辑,按字符读取标准输入,没有回声【英文标题】:Python, "filtered" line editing, read stdin by char with no echo 【发布时间】:2013-04-06 05:42:08 【问题描述】:

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

我说“缓冲区更改”而不是“读取字符”是因为,作为raw_input(),我希望它知道特殊键。例如,退格应该可以工作。

例如,如果我想使用回调来模拟输入的大写回显,代码将如下所示:

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

read_input(callback)

我怎样才能做到这一点?

注意: 我一直在尝试使用readlinecurses 来达到我的目的,但是这两个Python 绑定都不完整。 curses 在不清除整个屏幕的情况下无法启动,readline 在任何输入开始之前提供了一个挂钩。

【问题讨论】:

【参考方案1】:

嗯,我是手写代码的。我会留下一个解释以供将来参考。

要求

import sys, tty, termios, codecs, unicodedata
from contextlib import contextmanager

禁用行缓冲

简单地读取标准输入时出现的第一个问题是行缓冲。我们希望单个字符无需换行即可到达我们的程序,这不是终端操作的默认方式。

为此,我编写了一个处理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

解码标准输入

仅读取标准输入的第二个问题是编码。非 ascii unicode 字符会一个字节一个字节地传给我们,这是完全不可取的。

为了正确解码标准输入,我编写了一个生成器,我们可以对 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

【讨论】:

以上是关于Python,“过滤”行编辑,按字符读取标准输入,没有回声的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 C++ 中从标准输入读取行比 Python 慢得多?

按数据框中是不是存在字符串过滤行

我可以同时从 Node 的标准输入中读取行和字符吗?

从标准输入读取所有文本到字符串

Linux下的强大工具之一sed(转),Shell必备

Linux Shell学习-sed命令详解