Frage Verarbeiten Sie Escape-Sequenzen in einer Zeichenfolge in Python


Manchmal, wenn ich Eingaben von einer Datei oder dem Benutzer erhalte, erhalte ich eine Zeichenfolge mit Escape-Sequenzen. Ich möchte die Escape-Sequenzen verarbeiten genauso wie Python Escape-Sequenzen in String-Literalen verarbeitet.

Zum Beispiel, sagen wir mal myString ist definiert als:

>>> myString = "spam\\neggs"
>>> print(myString)
spam\neggs

Ich möchte eine Funktion (ich werde es nennen process) Das macht das:

>>> print(process(myString))
spam
eggs

Es ist wichtig, dass die Funktion alle Escape-Sequenzen in Python verarbeiten kann (aufgelistet in einer Tabelle im obigen Link).

Hat Python eine Funktion, dies zu tun?


75
2017-10-26 03:43


Ursprung


Antworten:


Die richtige Methode ist die Verwendung des 'string-escape' Codes, um die Zeichenfolge zu dekodieren.

>>> myString = "spam\\neggs"
>>> decoded_string = bytes(myString, "utf-8").decode("unicode_escape") # python3 
>>> decoded_string = myString.decode('string_escape') # python2
>>> print(decoded_string)
spam
eggs

Verwenden Sie nicht den AST oder eval. Die Verwendung der String-Codecs ist viel sicherer.


106
2017-10-26 05:01



unicode_escape funktioniert im Allgemeinen nicht

Es stellt sich heraus, dass die string_escape oder unicode_escape Lösung funktioniert im Allgemeinen nicht - insbesondere funktioniert es nicht in Gegenwart von tatsächlichem Unicode.

Wenn du dir sicher sein kannst jeden Nicht-ASCII-Zeichen werden mit Escapezeichen versehen (und denken Sie daran, dass alles, was über die ersten 128 Zeichen hinausgeht, kein ASCII-Zeichen ist), unicode_escape wird das Richtige für dich tun. Wenn sich in der Zeichenfolge jedoch bereits literale Nicht-ASCII-Zeichen befinden, werden die Dinge schief gehen.

unicode_escape ist im Grunde entworfen, um Bytes in Unicode-Text zu konvertieren. Aber an vielen Stellen - zum Beispiel Python-Quellcode - sind die Quelldaten bereits Unicode-Text.

Das funktioniert nur, wenn Sie den Text zuerst in Bytes codieren. UTF-8 ist die sinnvolle Kodierung für den gesamten Text, das sollte funktionieren, oder?

Die folgenden Beispiele sind in Python 3, so dass die String-Literale sauberer sind, aber das gleiche Problem existiert mit leicht unterschiedlichen Manifestationen auf Python 2 und 3.

>>> s = 'naïve \\t test'
>>> print(s.encode('utf-8').decode('unicode_escape'))
naïve   test

Nun, das ist falsch.

Die neue empfohlene Methode zum Verwenden von Codecs, die Text in Text decodieren, besteht im Anrufen codecs.decode direkt. Hilft das?

>>> import codecs
>>> print(codecs.decode(s, 'unicode_escape'))
naïve   test

Ganz und gar nicht. (Auch das obige ist ein UnicodeError auf Python 2.)

Das unicode_escape Codec, trotz seines Namens, geht davon aus, dass alle Nicht-ASCII-Bytes in der Latin-1 (ISO-8859-1) -Codierung sind. Also müsstest du es so machen:

>>> print(s.encode('latin-1').decode('unicode_escape'))
naïve    test

Aber das ist schrecklich. Dies beschränkt Sie auf die 256 Latin-1-Zeichen, als ob Unicode überhaupt nicht erfunden worden wäre!

>>> print('Ernő \\t Rubik'.encode('latin-1').decode('unicode_escape'))
UnicodeEncodeError: 'latin-1' codec can't encode character '\u0151'
in position 3: ordinal not in range(256)

Hinzufügen eines regulären Ausdrucks, um das Problem zu lösen

(Überraschenderweise haben wir jetzt keine zwei Probleme.)

Was wir tun müssen, ist nur die Anwendung unicode_escape Dekodierer zu Dingen, von denen wir sicher sind, dass sie ASCII-Text sind. Insbesondere können wir sicherstellen, dass es nur auf gültige Python-Escape-Sequenzen angewendet wird, bei denen es sich garantiert um ASCII-Text handelt.

Der Plan ist, wir finden Escape-Sequenzen mit einem regulären Ausdruck und verwenden eine Funktion als Argument für re.sub um sie durch ihren wertlosen Wert zu ersetzen.

import re
import codecs

ESCAPE_SEQUENCE_RE = re.compile(r'''
    ( \\U........      # 8-digit hex escapes
    | \\u....          # 4-digit hex escapes
    | \\x..            # 2-digit hex escapes
    | \\[0-7]{1,3}     # Octal escapes
    | \\N\{[^}]+\}     # Unicode characters by name
    | \\[\\'"abfnrtv]  # Single-character escapes
    )''', re.UNICODE | re.VERBOSE)

def decode_escapes(s):
    def decode_match(match):
        return codecs.decode(match.group(0), 'unicode-escape')

    return ESCAPE_SEQUENCE_RE.sub(decode_match, s)

Und damit:

>>> print(decode_escapes('Ernő \\t Rubik'))
Ernő     Rubik

75
2017-07-01 21:12



Die eigentlich richtige und bequeme Antwort für Python 3:

>>> import codecs
>>> myString = "spam\\neggs"
>>> print(codecs.escape_decode(bytes(myString, "utf-8"))[0].decode("utf-8"))
spam
eggs
>>> myString = "naïve \\t test"
>>> print(codecs.escape_decode(bytes(myString, "utf-8"))[0].decode("utf-8"))
naïve    test

Details bezüglich codecs.escape_decode:

  • codecs.escape_decode ist ein Byte-zu-Byte-Decoder
  • codecs.escape_decode entschlüsselt ASCII-Escape-Sequenzen wie: b"\\n" -> b"\n", b"\\xce" -> b"\xce".
  • codecs.escape_decode Es ist nicht wichtig oder nicht wichtig, über die Codierung des Byteobjekts Bescheid zu wissen, aber die Codierung der maskierten Bytes sollte mit der Codierung des restlichen Objekts übereinstimmen.

Hintergrund:

  • @rspeer ist richtig: unicode_escape ist die falsche Lösung für python3. Das ist weil unicode_escape dekodiert entkommete Bytes und dekodiert dann Bytes in eine Unicode-Zeichenkette, erhält aber keine Information darüber, welcher Codec für die zweite Operation verwendet werden soll.
  • @ Jerub ist richtig: Vermeiden Sie die AST oder eval.
  • Ich habe es zuerst entdeckt codecs.escape_decode von Diese Antwort auf "Wie kann ich .decode ('string-escape') in Python3?". Wie diese Antwort besagt, ist diese Funktion für Python 3 derzeit nicht dokumentiert.

13
2018-05-05 20:27



Das ast.literal_eval Die Funktion kommt zwar nahe, erwartet aber, dass die Zeichenfolge zuerst korrekt zitiert wird.

Natürlich hängt Pythons Interpretation von Backslash-Escapes davon ab, wie der String zitiert wird ("" vs r"" vs u"", Triple-Anführungszeichen usw.), so dass Sie die Benutzereingaben in geeignete Anführungszeichen einschließen und an sie übergeben möchten literal_eval. Wrapping in Anführungszeichen wird auch verhindern literal_eval aus der Rückgabe einer Zahl, Tupel, Wörterbuch, etc.

Es kann immer noch schwierig werden, wenn der Benutzer nicht notierte Anführungszeichen des Typs eingibt, den Sie um die Zeichenfolge umbrechen möchten.


5
2017-10-26 03:50



Der folgende Code sollte funktionieren, damit \ n in der Zeichenfolge angezeigt werden muss.

import string

our_str = 'The String is \\n, \\n and \\n!'
new_str = string.replace(our_str, '/\\n', '/\n', 1)
print(new_str)

0
2018-03-26 09:42



Wenn du der Quelle der Daten vertraust, füge einfach Zitate darum herum und eval () es?

>>> myString = 'spam\\neggs'
>>> print eval('"' + myString.replace('"','') + '"')
spam
eggs

PS. hinzugefügt böser Code-exec Gegenmaßnahme - jetzt wird es alles abziehen " vor dem Auswerten


-4
2017-10-26 03:49