如何从 python 中的字符串中删除 ANSI 转义序列
- 2024-12-20 08:38:00
- admin 原创
- 61
问题描述:
这是包含我的字符串的片段。
'ls
x1b[00mx1b[01;31mexamplefile.zipx1b[00m
x1b[01;31m'
该字符串是我执行的 SSH 命令返回的。我无法使用当前状态下的字符串,因为它包含 ANSI 标准化转义序列。我如何以编程方式删除转义序列,以便字符串仅剩下部分'examplefile.zip'
。
解决方案 1:
使用正则表达式删除它们:
import re
# 7-bit C1 ANSI sequences
ansi_escape = re.compile(r'''
x1B # ESC
(?: # 7-bit C1 Fe (except CSI)
[@-Z\\-_]
| # or [ for CSI, followed by a control sequence
[
[0-?]* # Parameter bytes
[ -/]* # Intermediate bytes
[@-~] # Final byte
)
''', re.VERBOSE)
result = ansi_escape.sub('', sometext)
或者,不使用VERBOSE
标志,采用压缩形式:
ansi_escape = re.compile(r'x1B(?:[@-Z\\-_]|[[0-?]*[ -/]*[@-~])')
result = ansi_escape.sub('', sometext)
演示:
>>> import re
>>> ansi_escape = re.compile(r'x1B(?:[@-Z\\-_]|[[0-?]*[ -/]*[@-~])')
>>> sometext = 'ls
x1b[00mx1b[01;31mexamplefile.zipx1b[00m
x1b[01;31m'
>>> ansi_escape.sub('', sometext)
'ls
examplefile.zip
'
上述正则表达式涵盖所有 7 位 ANSI C1 转义序列,但不包括8 位 C1 转义序列开启符。后者在当今的 UTF-8 世界中从未使用过,因为在当今的 UTF-8 世界中,相同范围的字节具有不同的含义。
如果你确实需要覆盖 8 位代码(然后大概使用bytes
值),那么正则表达式将变成这样的字节模式:
# 7-bit and 8-bit C1 ANSI sequences
ansi_escape_8bit = re.compile(br'''
(?: # either 7-bit C1, two bytes, ESC Fe (omitting CSI)
x1B
[@-Z\\-_]
| # or a single 8-bit byte Fe (omitting CSI)
[x80-x9Ax9C-x9F]
| # or CSI + control codes
(?: # 7-bit CSI, ESC [
x1B[
| # 8-bit CSI, 9B
x9B
)
[0-?]* # Parameter bytes
[ -/]* # Intermediate bytes
[@-~] # Final byte
)
''', re.VERBOSE)
result = ansi_escape_8bit.sub(b'', somebytesvalue)
可以浓缩为
# 7-bit and 8-bit C1 ANSI sequences
ansi_escape_8bit = re.compile(
br'(?:x1B[@-Z\\-_]|[x80-x9Ax9C-x9F]|(?:x1B[|x9B)[0-?]*[ -/]*[@-~])'
)
result = ansi_escape_8bit.sub(b'', somebytesvalue)
有关详细信息,请参阅:For more information, see:
维基百科上的ANSI转义代码概述
ECMA-48 标准,第 5 版(特别是第 5.3 和 5.4 节)
您给出的示例包含 4 个 CSI(控制序列引入器)代码,以x1B[
或ESC[
开头字节为标记,每个代码都包含一个 SGR(选择图形再现)代码,因为它们都以 结尾m
。这些之间的参数(以;
分号分隔)告诉您的终端要使用哪些图形再现属性。因此,对于每个x1B[....m
序列,使用的 3 个代码是:
0(或
00
在此示例中):重置,禁用所有属性1(或
01
在示例中):粗体31:红色(前景)
但是,ANSI 不仅仅只有 CSI SGR 代码。仅使用 CSI,您还可以控制光标、清除行或整个显示,或者滚动(当然,前提是终端支持此功能)。除了 CSI,还有用于选择替代字体(SS2
和SS3
)的代码,用于发送“私人消息”(例如密码),用于与终端(DCS
)、操作系统(OSC
)或应用程序本身(APC
、一种让应用程序将自定义控制代码搭载到通信流上的方式)进行通信,以及用于帮助定义字符串(SOS
、字符串开头、ST
字符串终止符)或将所有内容重置回基本状态(RIS
)的其他代码。上述正则表达式涵盖了所有这些内容。
但请注意,上述正则表达式仅删除 ANSI C1 代码,而不会删除这些代码可能标记的任何其他数据(例如 OSC 打开器和终止 ST 代码之间发送的字符串)。删除这些数据需要额外的工作,超出了本答案的范围。
解决方案 2:
接受的答案仅考虑了格式化为改变前景色和文本样式的 ANSI 标准化转义序列。许多序列不以 结尾'm'
,例如:光标定位、擦除和滚动区域。下面的模式试图涵盖除设置前景色和文本样式之外的所有情况。
以下是 ANSI 标准化控制序列的正则表达式:
/(x9B|x1B[)[0-?]*[ -/]*[@-~]/
其他参考资料:
ECMA-48 第 5.4 节
ANSI 转义码
解决方案 3:
功能
根据Martijn Pieters♦ 的回答和Jeff 的正则表达式。
def escape_ansi(line):
ansi_escape = re.compile(r'(?:x1B[@-_]|[x80-x9F])[0-?]*[ -/]*[@-~]')
return ansi_escape.sub('', line)
测试
def test_remove_ansi_escape_sequence(self):
line = ' /u001b[0;35mBlabla/u001b[0m /u001b[0;36m172.18.0.2/u001b[0m'
escaped_line = escape_ansi(line)
self.assertEqual(escaped_line, ' Blabla 172.18.0.2')
测试
如果您想自己运行它,请使用python3
(更好的 unicode 支持,blablabla)。测试文件应该是这样的:
import unittest
import re
def escape_ansi(line):
…
class TestStringMethods(unittest.TestCase):
def test_remove_ansi_escape_sequence(self):
…
if __name__ == '__main__':
unittest.main()
解决方案 4:
建议的正则表达式对我来说不起作用,所以我自己创建了一个。以下是我根据此处的规范创建的 Python 正则表达式
ansi_regex = r'x1b(' \n r'([??d+[hl])|' \n r'([=<>a-kzNM78])|' \n r'([()][a-b0-2])|' \n r'([d{0,2}[ma-dgkjqi])|' \n r'([d+;d+[hfy]?)|' \n r'([;?[hf])|' \n r'(#[3-68])|' \n r'([01356]n)|' \n r'(O[mlnp-z]?)|' \n r'(/Z)|' \n r'(d+)|' \n r'([?d;d0c)|' \n r'(d;dR))'
ansi_escape = re.compile(ansi_regex, flags=re.IGNORECASE)
我在以下代码片段上测试了我的正则表达式(基本上是从 ascii-table.com 页面复制粘贴的)
x1b[20h Set
x1b[?1h Set
x1b[?3h Set
x1b[?4h Set
x1b[?5h Set
x1b[?6h Set
x1b[?7h Set
x1b[?8h Set
x1b[?9h Set
x1b[20l Set
x1b[?1l Set
x1b[?2l Set
x1b[?3l Set
x1b[?4l Set
x1b[?5l Set
x1b[?6l Set
x1b[?7l Reset
x1b[?8l Reset
x1b[?9l Reset
x1b= Set
x1b> Set
x1b(A Set
x1b)A Set
x1b(B Set
x1b)B Set
x1b(0 Set
x1b)0 Set
x1b(1 Set
x1b)1 Set
x1b(2 Set
x1b)2 Set
x1bN Set
x1bO Set
x1b[m Turn
x1b[0m Turn
x1b[1m Turn
x1b[2m Turn
x1b[4m Turn
x1b[5m Turn
x1b[7m Turn
x1b[8m Turn
x1b[1;2 Set
x1b[1A Move
x1b[2B Move
x1b[3C Move
x1b[4D Move
x1b[H Move
x1b[;H Move
x1b[4;3H Move
x1b[f Move
x1b[;f Move
x1b[1;2 Move
x1bD Move/scroll
x1bM Move/scroll
x1bE Move
x1b7 Save
x1b8 Restore
x1bH Set
x1b[g Clear
x1b[0g Clear
x1b[3g Clear
x1b#3 Double-height
x1b#4 Double-height
x1b#5 Single
x1b#6 Double
x1b[K Clear
x1b[0K Clear
x1b[1K Clear
x1b[2K Clear
x1b[J Clear
x1b[0J Clear
x1b[1J Clear
x1b[2J Clear
x1b5n Device
x1b0n Response:
x1b3n Response:
x1b6n Get
x1b[c Identify
x1b[0c Identify
x1b[?1;20c Response:
x1bc Reset
x1b#8 Screen
x1b[2;1y Confidence
x1b[2;2y Confidence
x1b[2;9y Repeat
x1b[2;10y Repeat
x1b[0q Turn
x1b[1q Turn
x1b[2q Turn
x1b[3q Turn
x1b[4q Turn
x1b< Enter/exit
x1b= Enter
x1b> Exit
x1bF Use
x1bG Use
x1bA Move
x1bB Move
x1bC Move
x1bD Move
x1bH Move
x1b12 Move
x1bI
x1bK
x1bJ
x1bZ
x1b/Z
x1bOP
x1bOQ
x1bOR
x1bOS
x1bA
x1bB
x1bC
x1bD
x1bOp
x1bOq
x1bOr
x1bOs
x1bOt
x1bOu
x1bOv
x1bOw
x1bOx
x1bOy
x1bOm
x1bOl
x1bOn
x1bOM
x1b[i
x1b[1i
x1b[4i
x1b[5i
希望这可以帮助其他人:)
解决方案 5:
在我使用OSC 序列的情况下,所有正则表达式解决方案均不起作用( x1b]
)
要实际呈现可见的输出,你需要一个像pyte这样的终端仿真器
#! /usr/bin/env python3
import pyte # terminal emulator: render terminal output to visible characters
pyte_screen = pyte.Screen(80, 24)
pyte_stream = pyte.ByteStream(pyte_screen)
bytes_ = b''.join([
b'$ cowsay hello
', b'x1b[?2004l', b'
', b' _______
',
b'< hello >
', b' -------
', b' \\ ^__^
',
b' \\ (oo)\\_______
', b' (__)\\ )\\/\\
',
b' ||----w |
', b' || ||
',
b'x1b]0;user@laptop1:/tmpx1b\\', b'x1b]7;file://laptop1/tmpx1b\\', b'x1b[?2004h$ ',
])
pyte_stream.feed(bytes_)
# pyte_screen.display always has 80x24 characters, padded with whitespace
# -> use rstrip to remove trailing whitespace from all lines
text = ("".join([line.rstrip() + "
" for line in pyte_screen.display])).strip() + "
"
print("text", text)
print("cursor", pyte_screen.cursor.y, pyte_screen.cursor.x)
print("title", pyte_screen.title)
解决方案 6:
如果这对未来的 Stack Overflow 用户有帮助,我会使用crayons 库来让我的 Python 输出更具视觉冲击力,这很有利,因为它可以在 Windows 和 Linux 平台上运行。但是,我既要在屏幕上显示,又要将其附加到日志文件中,转义序列会影响日志文件的可读性,因此我想将其删除。但是,crayons 插入的转义序列产生了错误:
expected string or bytes-like object
解决方案是将参数转换为字符串,因此只需要对普遍接受的答案进行微小的修改:
def escape_ansi(line):
ansi_escape = re.compile(r'(x9B|x1B[)[0-?]*[ -/]*[@-~]')
return ansi_escape.sub('', str(line))
解决方案 7:
当我使用 pexect 时的情况。
child.sendline("ls backup")
child.expect(r"[0-9]{8}_[0-9]{4}.*")
print(child.after.split())
-> NG ['20231016_1603x1b[0m', 'x1b[01;34m20231016_1606x1b[0m']
* Add ls option
child.sendline("ls --color=never backup")
child.expect(r"[0-9]{8}_[0-9]{4}.*")
print(child.after.split())
-> OK! ['20231016_1603', '20231016_1606']
解决方案 8:
与正则表达式不同的是,此函数迭代字符串(更具体地说是流io.StringIO
)的字节以搜索CSI ,并“快进”直到找到x1b
最后一个字节。该函数将返回所有剩余字符。更具体地说,将其作为生成器产生。'm'
这仅适用于 ANSI颜色序列 。
代码:
import io
def strip_ansi_colour(text: str) -> iter:
"""Strip ANSI colour sequences from a string.
Args:
text (str): Text string to be stripped.
Returns:
iter[str]: A generator for each returned character. Note,
this will include newline characters.
"""
buff = io.StringIO(text)
while (b := buff.read(1)):
if b == 'x1b':
while (b := buff.read(1)) != 'm': continue
else:
yield b
使用 OP 字符串的示例:
>>> s = 'ls
x1b[00mx1b[01;31mexamplefile.zipx1b[00m
x1b[01;31m'
>>> ''.join(strip_ansi_colour(s))[2:].strip() # Trim ls and newlines
'examplefile.zip'
示例 2:
>>> s = 'x1b[93;mx1b[40;mx1b[22;m
Foo, spam and eggs.x1b[0m
'
>>> ''.join(strip_ansi_colour(s))
'
Foo, spam and eggs.
'
解决方案 9:
如果您想删除该`
`位,您可以通过此函数传递字符串(由 sarnold 编写):
def stripEscape(string):
""" Removes all escape sequences from the input string """
delete = ""
i=1
while (i<0x20):
delete += chr(i)
i += 1
t = string.translate(None, delete)
return t
但要小心,这会将转义序列前后的文本合并在一起。因此,使用 Martijn 的过滤字符串`'ls
examplefile.zip
',您将得到
lsexamplefile.zip。请注意
ls`所需文件名前面的。
我将首先使用 stripEscape 函数删除转义序列,然后将输出传递给 Martijn 的正则表达式,这将避免连接不需要的位。
解决方案 10:
对于 2020 年的 Python 3.5 来说,string.encode().decode('ascii')
ascii_string = 'ls
x1b[00mx1b[01;31mexamplefile.zipx1b[00m
x1b[01;31m'
decoded_string = ascii_string.encode().decode('ascii')
print(decoded_string)
>ls
>examplefile.zip
>
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理必备:盘点2024年13款好用的项目管理软件