实际上,答案比简单的格式化要复杂一些。
进程发送到终端的每个字符都可以看作是有限状态机(FSM)中的一个转换。这个FSM的状态大致对应于显示的句子和光标位置,但还有许多其他变量,例如终端的尺寸、当前输入的控制序列*、终端模式(例如VI模式/经典BASH控制台)等。
这个FSM的良好实现可以在pexpect源代码中看到。
回答我的问题,没有核心的Unix“函数”可以将字符串格式化为在终端中显示的内容,因为这样的函数特定于呈现进程输出的终端,您必须重新编写完整的终端来处理每个可能的字符和控制序列。
但是我们可以自己实现一个简单的函数。我们需要定义一个带有初始状态的FSM:
以及转换(输入字符):
- 任何字母数字/空格字符:用它本身替换光标位置的字符(如果没有则添加),并将光标位置增加
\x08
十六进制代码:将光标位置减少
并将字符串传递给它。
Python解决方案
def decode(input_string):
displayed_string = []
cursor_position = 0
for character in input_string:
if str.isalnum(character) or str.isspace(character):
displayed_string[cursor_position:cursor_position+1] = character
cursor_position += 1
elif character == "\x08":
cursor_position -= 1
else:
print("{} is not handled by this function".format(repr(character)))
return "".join(displayed_string)
和一个例子
>>> decode("test\x08 \x08")
tes
关于控制序列的说明
ANSI控制序列是一组字符,它们可以作为终端(显示器/光标/终端模式等)状态的转换。它可以被看作是我们FSM状态和转换的细化,具有更多的子状态和子转换。
例如:当您在经典Unix终端(如VT100)中按下UP键时,实际上输入的是控制序列:ESC 0 A
,其中ESC
是十六进制代码\x1b
。 ESC
转换到ESCAPE模式,在A之后返回正常模式。
一些进程将此序列解释为垂直光标位置的移动(VI),而其他进程则将其解释为在历史记录中向后移动(BASH):这完全取决于处理输入的程序。
然而,同样的序列可以用于输出过程,但它很可能会将光标向上移动屏幕:这取决于终端的实现。
一个好的ANSI控制序列列表可在这里找到。
'\r'
(即回车符),然后写入前面的字符串,但我认为这样会很麻烦。 - Wayne Werner