Das ist meine Zeichenfolge:
'ls\r\n\x1b[00m\x1b[01;31mexamplefile.zip\x1b[00m\r\n\x1b[01;31m'
Ich habe Code verwendet, um die Ausgabe eines SSH-Befehls abzurufen, und ich möchte, dass meine Zeichenfolge nur 'examplefile.zip' enthält.
Was kann ich verwenden, um die zusätzlichen Escape-Sequenzen zu entfernen?
python
string
escaping
ansi-escape
SpartaSixZero
quelle
quelle
Antworten:
Löschen Sie sie mit einem regulären Ausdruck:
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)
oder ohne
VERBOSE
Flagge in komprimierter 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'
Der obige reguläre Ausdruck deckt alle 7-Bit-ANSI-C1-Escape-Sequenzen ab, nicht jedoch die 8-Bit-C1-Escape-Sequenzöffner. Letztere werden in der heutigen UTF-8-Welt, in der derselbe Bytebereich eine andere Bedeutung hat, niemals verwendet.
Wenn Sie auch die 8-Bit-Codes abdecken müssen (und dann vermutlich mit
bytes
Werten arbeiten), wird der reguläre Ausdruck zu einem Bytemuster wie dem folgenden:# 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)
die bis zu verdichtet werden kann
# 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)
Weitere Informationen finden Sie unter:
Das von Ihnen angegebene Beispiel enthält 4 CSI-Codes (Control Sequence Introducer), die durch die
\x1B[
oder ESC-[
Öffnungsbytes gekennzeichnet sind, und enthält jeweils einen SGR-Code (Select Graphic Rendition), da diese jeweils mit endenm
. Die Parameter (durch;
Semikolons getrennt ) dazwischen teilen Ihrem Terminal mit, welche Grafikwiedergabeattribute verwendet werden sollen. Für jede\x1B[....m
Sequenz werden also drei Codes verwendet:00
in diesem Beispiel): Zurücksetzen , alle Attribute deaktivieren01
im Beispiel): fettANSI bietet jedoch mehr als nur CSI-SGR-Codes. Mit CSI allein können Sie auch den Cursor steuern, Linien oder die gesamte Anzeige löschen oder scrollen (vorausgesetzt, das Terminal unterstützt dies natürlich). Über CSI hinaus gibt es Codes, mit denen Sie alternative Schriftarten (
SS2
undSS3
) auswählen , "private Nachrichten" senden (Passwörter denken), mit dem Terminal (DCS
), dem Betriebssystem (OSC
) oder der Anwendung selbst kommunizieren können (APC
eine Möglichkeit für Anwendungen) Huckepack-benutzerdefinierte Steuercodes für den Kommunikationsstrom) und weitere Codes zum Definieren von Zeichenfolgen (SOS
, Beginn der Zeichenfolge, ZeichenfolgenabschlussST
) oder zum Zurücksetzen aller Elemente auf einen Basiszustand (RIS
). Die obigen regulären Ausdrücke decken all dies ab.Beachten Sie, dass der obige reguläre Ausdruck jedoch nur die ANSI C1-Codes entfernt und keine zusätzlichen Daten, die diese Codes möglicherweise markieren (z. B. die zwischen einem OSC-Öffner und dem abschließenden ST-Code gesendeten Zeichenfolgen). Das Entfernen dieser würde zusätzliche Arbeit außerhalb des Rahmens dieser Antwort erfordern.
quelle
Die akzeptierte Antwort auf diese Frage berücksichtigt nur Farb- und Schrifteffekte. Es gibt viele Sequenzen, die nicht mit 'm' enden, z. B. Cursorpositionierung, Löschen und Scrollen.
Der vollständige reguläre Ausdruck für Kontrollsequenzen (auch bekannt als ANSI-Escape-Sequenzen) lautet
/(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]/
Siehe ECMA-48, Abschnitt 5.4 und ANSI-Escape-Code
quelle
bluetoothctl
, die beispielsweise von: erstellt wurden\x1b[0;94m
. Herstellung des Ausdruck Groß- und Kleinschreibung oder Ersetzen1B
mit1b
in dem Muster keinen Unterschied gemacht. Ich benutze Python und die Liniere.compile(r'/(\x9b|\x1b\[)[0-?]*[ -\/]*[@-~]/', re.I)
. Dann mache ich,pattern.sub("", my_string)
was nichts bewirkt. Mache ich etwas falsch?/.../
ist keine Python-Syntax , sondern eine Syntax, die Sie in VI oder Perl oder awk verwenden würden. 2) Der\x9B
Opener (für CSI-Codes) ist nicht mit UTF-8 kompatibel und wird daher nur noch selten verwendet. ESC[
wird bevorzugt. 3) Ihr Muster deckt nur CSI-Codes ab, nicht den gesamten Bereich der ANSI-Escapezeichen (einschließlich OSC) Thomas Dickly erwähnt, aber auch SS2, SS3, DCS, ST, OSC, SOS, PM, APC und RIS!Funktion
Basierend auf der Antwort von Martijn Pieters ♦ mit Jeffs regulärem Ausdruck .
def escape_ansi(line): ansi_escape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]') return ansi_escape.sub('', line)
Prüfung
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')
Testen
Wenn Sie es selbst ausführen möchten, verwenden Sie
python3
(bessere Unicode-Unterstützung, blablabla). So sollte die Testdatei sein:import unittest import re def escape_ansi(line): … class TestStringMethods(unittest.TestCase): def test_remove_ansi_escape_sequence(self): … if __name__ == '__main__': unittest.main()
quelle
/
Escapezeichen im vorletzten Zeichensatz verlassen[ -\/]
?[ -/]
wird ausreichen.Die vorgeschlagene Regex hat mir nicht geholfen, also habe ich eine eigene erstellt. Das Folgende ist ein Python-Regex, den ich basierend auf der hier gefundenen Spezifikation erstellt habe
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)
Ich habe meinen regulären Ausdruck auf dem folgenden Snippet getestet (im Grunde genommen ein Kopieren und Einfügen von der Seite 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
Hoffentlich hilft das anderen :)
quelle
O
(SS3) versetzt das Terminal in einen alternativen Schriftmodus, und das nächste Byte wird in diesem bestimmten Modus interpretiert. Die möglichen Werte in diesem Modus sind nicht beschränkt aufm
,n
,l
, oderp
durchz
. Ich würde das Byte nach SS3 nicht einmal entfernen. SS2 ist im Grunde die gleiche Funktionalität (nur eine andere Schriftart), aber Ihre Regex zieht nicht das nächste Byte ein.m
letzte Byte zurückbleibt .Für 2020 mit Python 3.5 ist es so einfach wie
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 >
quelle
Wenn es zukünftigen Stapelüberläufern hilft, habe ich die Buntstiftbibliothek verwendet , um meiner Python-Ausgabe eine etwas visuellere Wirkung zu verleihen. Dies ist vorteilhaft, da sie sowohl auf Windows- als auch auf Linux-Plattformen funktioniert. Ich habe jedoch sowohl auf dem Bildschirm angezeigt als auch Protokolldateien angehängt, und die Escape-Sequenzen haben die Lesbarkeit der Protokolldateien beeinträchtigt. Deshalb wollte ich sie entfernen. Die durch Buntstifte eingefügten Escape-Sequenzen führten jedoch zu einem Fehler:
expected string or bytes-like object
Die Lösung bestand darin, den Parameter in eine Zeichenfolge umzuwandeln, sodass nur eine geringfügige Änderung der allgemein akzeptierten Antwort erforderlich war:
def escape_ansi(line): ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]') return ansi_escape.sub('', str(line))
quelle
Wenn Sie das
\r\n
Bit entfernen möchten , können Sie die Zeichenfolge über diese Funktion ( geschrieben von sarnold ) übergeben: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
Vorsicht, dies wird den Text vor und hinter den Escape-Sequenzen zusammenfassen. Wenn
'ls\r\nexamplefile.zip\r\n'
Sie also Martijns gefilterten String verwenden , erhalten Sielsexamplefile.zip
. Beachten Sie denls
vor dem gewünschten Dateinamen.Ich würde zuerst die Funktion stripEscape verwenden, um die Escape-Sequenzen zu entfernen, und dann die Ausgabe an Martijns regulären Ausdruck übergeben, wodurch eine Verkettung des unerwünschten Bits vermieden würde.
quelle
string.translate()
Option von sarnold ist auch nicht gerade idiomatisch (warum sollte sie verwendet werden,while
wennfor
sie beendet istxrange()
, z. B.''.join([chr(i) for i in range(0x20)])
) und gilt nicht für Python 3 (wo Sie sie nurdict.fromkeys(range(0x20)))
alsstring.translate()
Karte verwenden könnten ).