如何从python中的字符串中删除ANSI转义序列

这是我的字符串:

'ls\r\n\x1b[00m\x1b[01;31mexamplefile.zip\x1b[00m\r\n\x1b[01;31m'

我正在使用代码从SSH命令检索输出,并且我希望我的字符串仅包含'examplefile.zip'

如何删除多余的转义序列?

SpartaSixZero asked 2019-11-13T11:12:58Z
6个解决方案
103 votes

使用正则表达式删除它们:

import re

# 7-bit C1 ANSI sequences
ansi_escape = re.compile(r'''
    \x1B    # ESC
    [@-_]   # 7-bit C1 Fe
    [0-?]*  # Parameter bytes
    [ -/]*  # Intermediate bytes
    [@-~]   # Final byte
''', re.VERBOSE)
result = ansi_escape.sub('', sometext)

或者,不带SS2标志,为简明形式:

ansi_escape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]')
result = ansi_escape.sub('', sometext)

演示:

>>> import re
>>> ansi_escape = re.compile(r'\x1B[@-_][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世界中从未使用过,在UTF-8世界中,相同范围的字节具有不同的含义。

如果确实也需要覆盖8位代码(然后大概使用SS2值),则正则表达式将变成如下所示的字节模式:

# 7-bit and 8-bit C1 ANSI sequences
ansi_escape_8bit = re.compile(br'''
    (?: # either 7-bit C1, two bytes, ESC Fe
        \x1B
        [@-_]
    |   # or a single 8-bit byte Fe
        [\x80-\x9F]
    )
    [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[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]')
result = ansi_escape_8bit.sub(b'', somebytesvalue)

有关更多信息,请参见:

  • Wikipedia上的ANSI转义代码概述
  • ECMA-48标准,第5版(尤其是第5.3和5.3节)

您给出的示例包含4个CSI(控制序列介绍者)代码(由SS2或ESC SS2开头字节标记),并且每个都包含一个SGR(选择图形呈现)代码,因为它们均以SS3结尾。参数(由2612311207335935953410分隔) 这些之间的分号告诉您的终端要使用哪些图形再现属性。 因此,对于每个2612311207335935953411序列,使用的3个代码是:

  • 0(或本示例中的SS2):重置,禁用所有属性
  • 1(或示例中的SS2):粗体
  • 31:红色(前景)

但是,ANSI不仅限于CSI SGR代码。 仅使用CSI,您还可以控制光标,清除行或整个显示或滚动(当然,前提是终端支持此功能)。 除CSI以外,还有一些代码可以选择其他字体(SS2SS3),发送“私人消息”(例如密码),与终端(DCS),操作系统(26123112073359534534)或应用程序本身(26123112073359534534) 一种应用程序将自定义控制代码附加到通信流上的方式),以及进一步的代码,以帮助定义字符串(SOS,字符串开始,ST字符串终止符)或将所有内容重置为基本状态(RIS)。 以上正则表达式涵盖了所有这些。

Martijn Pieters answered 2019-11-13T11:14:37Z
42 votes

该问题的公认答案仅考虑颜色和字体效果。 有很多不以“ m”结尾的序列,例如光标定位,擦除和滚动区域。

控制序列(又名ANSI转义序列)的完整正则表达式为

/(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]/

请参阅ECMA-48第5.4节和ANSI转义码

Jeff answered 2019-11-13T11:15:20Z
25 votes

功能

基于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 = '\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()
Édouard Lopez answered 2019-11-13T11:16:05Z
7 votes

建议的正则表达式对我没有帮助,所以我创建了自己的正则表达式。以下是我根据此处找到的规范创建的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

希望这会帮助其他人:)

kfir answered 2019-11-13T11:16:44Z
-1 votes

如果它对将来的Stack Overflowers有帮助,我正在使用crayons库为我的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))
Rory answered 2019-11-13T11:17:18Z
-3 votes

如果要删除'ls\r\nexamplefile.zip\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的正则表达式,这将避免串联不需要的位。

Neodied answered 2019-11-13T11:18:00Z
translate from https://stackoverflow.com:/questions/14693701/how-can-i-remove-the-ansi-escape-sequences-from-a-string-in-python