【问题标题】:Apply control characters to a string - Python将控制字符应用于字符串 - Python
【发布时间】:2016-08-03 05:45:09
【问题描述】:

我正在尝试将控制字符(例如应该删除前面字符的 '\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'(即回车),然后写入前面的字符串,但我认为这会很尴尬。
  • 控制字符取决于终端类型,它们保存在 terminfo 或 termcap 数据库中。因此,python 将不得不使用这些数据库将 \x08 转换为您认为它对您的特定终端类型意味着什么。如果你只使用这些字符中的一小部分,那么你可以自己弄清楚。这些数据来自哪里?另见docs.python.org/3/library/termios.html#module-termios

标签: python string backspace control-characters repr


【解决方案1】:

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

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

可以在pexpect source code 中看到此 FSM 的良好实现。

回答我的问题,没有核心 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 控制序列列表可用here

【讨论】:

  • 感谢您对自己的问题给出如此详细的回答。帮助了我!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-13
  • 1970-01-01
  • 2014-03-03
  • 2020-02-02
  • 1970-01-01
  • 2018-12-21
相关资源
最近更新 更多