¿Cómo puedo eliminar las secuencias de escape ANSI de una cadena en Python?

Esta es mi cadena:

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

Estaba usando el código para recuperar la salida de un comando SSH y quiero que mi cadena solo contenga ‘examplefile.zip’

¿Qué puedo usar para eliminar las secuencias de escape adicionales?

Eliminarlos con una expresión regular:

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

Manifestación:

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

(He ordenado la expresión de secuencia de escape para seguir la descripción general de Wikipedia de los códigos de escape ANSI , centrándome en las secuencias de CSI e ignorando los códigos C1, ya que nunca se usan en el mundo UTF-8 de hoy).

La respuesta aceptada a esta pregunta solo considera los efectos de color y fuente. Hay muchas secuencias que no terminan en ‘m’, como el posicionamiento del cursor, el borrado y las regiones de desplazamiento.

La regexp completa para las secuencias de control (también conocidas como secuencias de escape ANSI) es

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

Consulte la sección 5.4 de ECMA-48 y el código de escape ANSI

Función

Basado en la respuesta de Martijn Pieters ♦ con la expresión regular de Jeff .

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

Prueba

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

Pruebas

Si quieres ejecutarlo por ti mismo, usa python3 (mejor soporte de Unicode, blablabla). Aquí es cómo debe ser el archivo de prueba:

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

La expresión regular sugerida no me sirvió, así que creé una propia. La siguiente es una expresión regular de python que creé según la especificación que se encuentra aquí

 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) 

Probé mi expresión regular en el siguiente fragmento de código (básicamente una copia de la página 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 

Esperemos que esto ayude a otros 🙂

Si ayuda a los Stack Overflowers del futuro, estaba usando la biblioteca de crayones para darle a mi salida de Python un poco más de impacto visual, lo cual es ventajoso ya que funciona en plataformas Windows y Linux. Sin embargo, estaba mostrando tanto en pantalla como adjuntándome a los archivos de registro, y las secuencias de escape estaban afectando la legibilidad de los archivos de registro, así que quería eliminarlos. Sin embargo, las secuencias de escape insertadas por los crayones produjeron un error:

 expected string or bytes-like object 

La solución fue convertir el parámetro en una cadena, por lo que solo se necesitaba una pequeña modificación de la respuesta comúnmente aceptada:

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

Si desea eliminar el bit \r\n , puede pasar la cadena a través de esta función ( escrita por 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 

Sin embargo, con cuidado, esto agrupará el texto delante y detrás de las secuencias de escape. Entonces, al usar la cadena filtrada de Martijn 'ls\r\nexamplefile.zip\r\n' , obtendrás lsexamplefile.zip . Tenga en cuenta la ls delante del nombre de archivo deseado.

Primero usaría la función stripEscape para eliminar las secuencias de escape, luego pasaría la salida a la expresión regular de Martijn, lo que evitaría concatenar el bit no deseado.