这是我的字符串:
'ls\r\n\x1b[00m\x1b[01;31mexamplefile.zip\x1b[00m\r\n\x1b[01;31m'
我正在使用代码从 SSH 命令检索输出,并且我希望我的字符串仅包含“examplefile.zip”
我可以用什么来删除额外的转义序列?
这是我的字符串:
'ls\r\n\x1b[00m\x1b[01;31mexamplefile.zip\x1b[00m\r\n\x1b[01;31m'
我正在使用代码从 SSH 命令检索输出,并且我希望我的字符串仅包含“examplefile.zip”
我可以用什么来删除额外的转义序列?
用正则表达式删除它们:
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\r\n\x1b[00m\x1b[01;31mexamplefile.zip\x1b[00m\r\n\x1b[01;31m'
>>> ansi_escape.sub('', sometext)
'ls\r\nexamplefile.zip\r\n'
上面的正则表达式涵盖了所有 7 位 ANSI C1 转义序列,但不包括 8 位 C1 转义序列开始符。后者在今天的 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-\x9A\x9C-\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-\x9A\x9C-\x9F]|(?:\x1B\[|\x9B)[0-?]*[ -/]*[@-~])'
)
result = ansi_escape_8bit.sub(b'', somebytesvalue)
有关更多信息,请参阅:
您给出的示例包含 4 个 CSI(控制序列引入器)代码,由\x1B[
或ESC[
开始字节标记,每个都包含一个 SGR(选择图形再现)代码,因为它们每个都以m
. 这些参数之间的参数(用;
分号分隔)告诉您的终端要使用哪些图形再现属性。因此,对于每个\x1B[....m
序列,使用的 3 个代码是:
00
在本例中):重置,禁用所有属性01
在示例中):粗体然而,ANSI 不仅仅是 CSI SGR 代码。单独使用 CSI,您还可以控制光标、清除线条或整个显示或滚动(当然前提是终端支持)。除了 CSI,还有一些代码可以选择替代字体(SS2
和SS3
),发送“私人消息”(想想密码),与终端(DCS
)、操作系统(OSC
)或应用程序本身APC
(将自定义控制代码捎带到通信流上),以及帮助定义字符串(SOS
、字符串开始、ST
字符串终止符)或将所有内容重置回基本状态(RIS
)的更多代码。上面的正则表达式涵盖了所有这些。
请注意,上面的正则表达式仅删除了 ANSI C1 代码,但并未删除这些代码可能标记的任何其他数据(例如在 OSC 开启程序和终止 ST 代码之间发送的字符串)。删除这些将需要超出此答案范围的额外工作。
这个问题的公认答案只考虑颜色和字体效果。有很多序列不以“m”结尾,例如光标定位、擦除和滚动区域。
控制序列(又名 ANSI 转义序列)的完整正则表达式是
/(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]/
基于Martijn Pieters♦ 对Jeff的 regexp的回答。
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 = '\t\u001b[0;35mBlabla\u001b[0m \u001b[0;36m172.18.0.2\u001b[0m'
escaped_line = escape_ansi(line)
self.assertEqual(escaped_line, '\tBlabla 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()
建议的正则表达式对我没有用,所以我自己创建了一个。以下是我根据此处找到的规范创建的 python 正则表达式
ansi_regex = r'\x1b(' \
r'(\[\??\d+[hl])|' \
r'([=<>a-kzNM78])|' \
r'([\(\)][a-b0-2])|' \
r'(\[\d{0,2}[ma-dgkjqi])|' \
r'(\[\d+;\d+[hfy]?)|' \
r'(\[;?[hf])|' \
r'(#[3-68])|' \
r'([01356]n)|' \
r'(O[mlnp-z]?)|' \
r'(/Z)|' \
r'(\d+)|' \
r'(\[\?\d;\d0c)|' \
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
希望这对其他人有帮助:)
如果它对未来的 Stack Overflowers 有帮助,我会使用蜡笔库为我的 Python 输出提供更多视觉冲击力,这在 Windows 和 Linux 平台上都可以使用,这是有利的。但是,我既要在屏幕上显示又要附加到日志文件,并且转义序列会影响日志文件的易读性,因此想将它们删除。但是蜡笔插入的转义序列产生了错误:
expected string or bytes-like object
解决方案是将参数转换为字符串,因此只需对普遍接受的答案进行微小修改:
def escape_ansi(line):
ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]')
return ansi_escape.sub('', str(line))
如果要删除该\r\n
位,可以通过此函数(由 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\r\nexamplefile.zip\r\n'
,你会得到lsexamplefile.zip
. 注意ls
所需文件名前面的 。
我会首先使用 stripEscape 函数删除转义序列,然后将输出传递给 Martijn 的正则表达式,这样可以避免连接不需要的位。
对于 2020 年的 python 3.5,它就像string.encode().decode('ascii')
ascii_string = 'ls\r\n\x1b[00m\x1b[01;31mexamplefile.zip\x1b[00m\r\n\x1b[01;31m'
decoded_string = ascii_string.encode().decode('ascii')
print(decoded_string)
>ls
>examplefile.zip
>