将控制字符应用于字符串 - Python

9

我将尝试使用控制字符,例如 '\x08 \x08',它应该从字符串中删除前一个字符(向后移动,写空格,向后移动)
例如,当我在Python控制台中输入:

s = "test\x08 \x08"
print s
print repr(s)

我在终端中看到如下信息:
tes
'test\x08 \x08'

我正在寻找一个名为“函数”的函数,它可以将控制字符应用到我的字符串中:
v = function("test\x08 \x08")
sys.stdout.write(v)
sys.stdout.write(repr(v))

所以我得到了一个不含控制字符的 “干净” 字符串:

tes
tes

我知道在终端中,这部分由客户端处理,也许有一种方法可以使用核心Unix函数获取显示的字符串。

echo -e 'test\x08 \x08'
cat file.out # control char are here handled by the client
>> tes
cat -v file.out # which prints the "actual" content of the file
>> test^H ^H

你可以将退格键处理为 '\r'(即回车符),然后写入前面的字符串,但我认为这样会很麻烦。 - Wayne Werner
控制字符取决于终端类型,这些字符保存在terminfo或termcap数据库中。因此,Python必须使用这些数据库将\x08转换为您特定终端类型所需的字符。如果您只使用少量这些字符,则可以自己找出解决方法。这些数据来自哪里?请参见https://docs.python.org/3/library/termios.html#module-termios - cdarke
1个回答

5

实际上,答案比简单的格式化要复杂一些。

进程发送到终端的每个字符都可以看作是有限状态机(FSM)中的一个转换。这个FSM的状态大致对应于显示的句子和光标位置,但还有许多其他变量,例如终端的尺寸、当前输入的控制序列*、终端模式(例如VI模式/经典BASH控制台)等。

这个FSM的良好实现可以在pexpect源代码中看到。

回答我的问题,没有核心的Unix“函数”可以将字符串格式化为在终端中显示的内容,因为这样的函数特定于呈现进程输出的终端,您必须重新编写完整的终端来处理每个可能的字符和控制序列。

但是我们可以自己实现一个简单的函数。我们需要定义一个带有初始状态的FSM:

  • 显示的字符串:" "(空字符串)
  • 光标位置:0

以及转换(输入字符):

  • 任何字母数字/空格字符:用它本身替换光标位置的字符(如果没有则添加),并将光标位置增加
  • \x08 十六进制代码:将光标位置减少

并将字符串传递给它。

Python解决方案

def decode(input_string):

    # Initial state
    # String is stored as a list because
    # python forbids the modification of
    # a string
    displayed_string = [] 
    cursor_position = 0

    # Loop on our input (transitions sequence)
    for character in input_string:

        # Alphanumeric transition
        if str.isalnum(character) or str.isspace(character):
            # Add the character to the string
            displayed_string[cursor_position:cursor_position+1] = character 
            # Move the cursor forward
            cursor_position += 1

        # Backward transition
        elif character == "\x08":
            # Move the cursor backward
            cursor_position -= 1
        else:
            print("{} is not handled by this function".format(repr(character)))

    # We transform our "list" string back to a real string
    return "".join(displayed_string)

和一个例子

>>> decode("test\x08 \x08")
tes 

关于控制序列的说明

ANSI控制序列是一组字符,它们可以作为终端(显示器/光标/终端模式等)状态的转换。它可以被看作是我们FSM状态和转换的细化,具有更多的子状态和子转换。

例如:当您在经典Unix终端(如VT100)中按下UP键时,实际上输入的是控制序列:ESC 0 A,其中ESC是十六进制代码\x1bESC转换到ESCAPE模式,在A之后返回正常模式。

一些进程将此序列解释为垂直光标位置的移动(VI),而其他进程则将其解释为在历史记录中向后移动(BASH):这完全取决于处理输入的程序。

然而,同样的序列可以用于输出过程,但它很可能会将光标向上移动屏幕:这取决于终端的实现。

一个好的ANSI控制序列列表可在这里找到。


1
感谢您对自己问题的详细回答。帮了我大忙! - curious_weather

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接