How can I remove the ANSI escape sequences from a string in python How can I remove the ANSI escape sequences from a string in python python python

How can I remove the ANSI escape sequences from a string in python


Delete them with a regular expression:

import re# 7-bit C1 ANSI sequencesansi_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)

or, without the VERBOSE flag, in condensed form:

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

Demo:

>>> 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'

The above regular expression covers all 7-bit ANSI C1 escape sequences, but not the 8-bit C1 escape sequence openers. The latter are never used in today's UTF-8 world where the same range of bytes have a different meaning.

If you do need to cover the 8-bit codes too (and are then, presumably, working with bytes values) then the regular expression becomes a bytes pattern like this:

# 7-bit and 8-bit C1 ANSI sequencesansi_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)

which can be condensed down to

# 7-bit and 8-bit C1 ANSI sequencesansi_escape_8bit = re.compile(    br'(?:\x1B[@-Z\\-_]|[\x80-\x9A\x9C-\x9F]|(?:\x1B\[|\x9B)[0-?]*[ -/]*[@-~])')result = ansi_escape_8bit.sub(b'', somebytesvalue)

For more information, see:

The example you gave contains 4 CSI (Control Sequence Introducer) codes, as marked by the \x1B[ or ESC [ opening bytes, and each contains a SGR (Select Graphic Rendition) code, because they each end in m. The parameters (separated by ; semicolons) in between those tell your terminal what graphic rendition attributes to use. So for each \x1B[....m sequence, the 3 codes that are used are:

  • 0 (or 00 in this example): reset, disable all attributes
  • 1 (or 01 in the example): bold
  • 31: red (foreground)

However, there is more to ANSI than just CSI SGR codes. With CSI alone you can also control the cursor, clear lines or the whole display, or scroll (provided the terminal supports this of course). And beyond CSI, there are codes to select alternative fonts (SS2 and SS3), to send 'private messages' (think passwords), to communicate with the terminal (DCS), the OS (OSC), or the application itself (APC, a way for applications to piggy-back custom control codes on to the communication stream), and further codes to help define strings (SOS, Start of String, ST String Terminator) or to reset everything back to a base state (RIS). The above regexes cover all of these.

Note that the above regex only removes the ANSI C1 codes, however, and not any additional data that those codes may be marking up (such as the strings sent between an OSC opener and the terminating ST code). Removing those would require additional work outside the scope of this answer.


The accepted answer to this question only considers color and font effects. There are a lot of sequences that do not end in 'm', such as cursor positioning, erasing, and scroll regions.

The complete regexp for Control Sequences (aka ANSI Escape Sequences) is

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

Refer to ECMA-48 Section 5.4 and ANSI escape code


Function

Based on Martijn Pieters♦'s answer with Jeff's regexp.

def escape_ansi(line):    ansi_escape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]')    return ansi_escape.sub('', line)

Test

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')

Testing

If you want to run it by yourself, use python3 (better unicode support, blablabla). Here is how the test file should be:

import unittestimport redef escape_ansi(line):    …class TestStringMethods(unittest.TestCase):    def test_remove_ansi_escape_sequence(self):    …if __name__ == '__main__':    unittest.main()