web-dev-qa-db-fra.com

Comment puis-je supprimer les séquences d'échappement ANSI d'une chaîne dans python

Voici ma chaîne:

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

J'utilisais du code pour récupérer la sortie d'une commande SSH et je veux que ma chaîne contienne uniquement 'examplefile.Zip'

Que puis-je utiliser pour supprimer les séquences d'échappement supplémentaires?

53
SpartaSixZero

Supprimez-les avec une expression régulière:

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)

ou, sans l'indicateur VERBOSE, sous forme condensée:

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

Démo:

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

L'expression régulière ci-dessus couvre toutes les séquences d'échappement ANSI C1 7 bits, mais pas les ouvreurs de séquence d'échappement C1 8 bits. Ces derniers ne sont jamais utilisés dans le monde UTF-8 actuel où la même plage d'octets a une signification différente.

Si vous avez également besoin de couvrir les codes 8 bits (et que vous travaillez alors vraisemblablement avec des valeurs bytes), l'expression régulière devient un modèle d'octets comme celui-ci:

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

qui peut être condensé jusqu'à

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

Pour plus d'informations, voir:

L'exemple que vous avez donné contient 4 codes CSI (Control Sequence Introducer), marqués par le \x1B[ ou ESC [ octets d'ouverture, et chacun contient un code SGR (Select Graphic Rendition), car ils se terminent chacun par m. Les paramètres (séparés par ; points-virgules) entre ceux-ci indiquent à votre terminal les attributs de rendu graphique à utiliser. Donc pour chaque \x1B[....m séquence, les 3 codes utilisés sont:

  • 0 (ou 00 dans cet exemple): reset , désactivez tous les attributs
  • 1 (ou 01 dans l'exemple): bold
  • 31: rouge (premier plan)

Cependant, ANSI ne se limite pas aux codes CSI SGR. Avec CSI seul, vous pouvez également contrôler le curseur, effacer les lignes ou tout l'affichage, ou faire défiler (à condition que le terminal le prenne bien sûr en charge). Et au-delà de CSI, il existe des codes pour sélectionner des polices alternatives (SS2 et SS3), pour envoyer des "messages privés" (pensez aux mots de passe), pour communiquer avec le terminal (DCS), le système d'exploitation (OSC) ou l'application elle-même (APC, un moyen pour les applications de superposer des codes de contrôle personnalisés sur le flux de communication) et d'autres codes pour aider à définir des chaînes (SOS, début de chaîne, ST terminaison de chaîne) ou à réinitialiser tout à un état de base (RIS). Les expressions rationnelles ci-dessus couvrent tous ces éléments.

99
Martijn Pieters

La réponse acceptée à cette question ne prend en compte que les effets de couleur et de police. Il y a beaucoup de séquences qui ne se terminent pas par "m", comme le positionnement du curseur, l'effacement et les régions de défilement.

Le regexp complet pour les séquences de contrôle (alias séquences d'échappement ANSI) est

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

Reportez-vous à ECMA-48 section 5.4 et code d'échappement ANSI

42
Jeff

Une fonction

Basé sur réponse de Martijn Pieters ♦ avec regexp de Jeff .

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

Tester

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

Essai

Si vous souhaitez l'exécuter par vous-même, utilisez python3 (meilleur support unicode, blablabla). Voici comment le fichier de test doit être:

import unittest
import re

def escape_ansi(line):
    …

class TestStringMethods(unittest.TestCase):
    def test_remove_ansi_escape_sequence(self):
    …

if __== '__main__':
    unittest.main()
25
Édouard Lopez

Le regex suggéré n'a pas fait l'affaire pour moi, alors j'ai créé le mien. Ce qui suit est un python regex que j'ai créé sur la base des spécifications trouvées ici

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)

J'ai testé mon expression régulière sur l'extrait de code suivant (essentiellement un copier-coller de la page 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

J'espère que cela aidera les autres :)

7
kfir