Czasami, gdy otrzymuję dane wejściowe z pliku lub użytkownika, otrzymuję ciąg znaków z sekwencjami ucieczki. Chciałbym przetwarzać sekwencje specjalne w taki sam sposób, w jaki Python przetwarza sekwencje specjalne w literałach łańcuchowych .
Na przykład, powiedzmy, że myString
jest zdefiniowany jako:
>>> myString = "spam\\neggs"
>>> print(myString)
spam\neggs
Chcę funkcji (tak ją nazywam process
), która robi to:
>>> print(process(myString))
spam
eggs
Ważne jest, aby funkcja mogła przetwarzać wszystkie sekwencje specjalne w Pythonie (wymienione w tabeli w powyższym linku).
Czy Python ma do tego funkcję?
'spam'+"eggs"+'''some'''+"""more"""
zostanie przetworzony ciąg zawierający ?myString = "'spam'+\"eggs\"+'''some'''+\"\"\"more\"\"\""
,print(bytes(myString, "utf-8").decode("unicode_escape"))
Wydaje się działać.Odpowiedzi:
Właściwą rzeczą do zrobienia jest użycie kodu „ucieczki ciągu” do zdekodowania łańcucha.
Nie używaj AST ani eval. Korzystanie z kodeków tekstowych jest znacznie bezpieczniejsze.
źródło
'string\W+escape'
Notice that spelling alternatives that only differ in case or use a hyphen instead of an underscore are also valid aliases; therefore, e.g. 'utf-8' is a valid alias for the 'utf_8' codec.
>>> print("juancarlo\\tañez".encode('utf-8').decode('unicode_escape'))
Otrzymasz:juancarlo añez
latin1
jest zakładane przezunicode_escape
, powtórz bit kodowania / dekodowania, np.s.encode('utf-8').decode('unicode_escape').encode('latin1').decode('utf8')
unicode_escape
ogólnie nie działaOkazuje się, że rozwiązanie
string_escape
lubunicode_escape
ogólnie nie działa - w szczególności nie działa w obecności rzeczywistego Unicode.Jeśli możesz być pewien, że każdy znak spoza ASCII zostanie usunięty (i pamiętaj, że wszystko poza pierwszymi 128 znakami nie jest ASCII),
unicode_escape
zrobi to dobrze. Ale jeśli w twoim ciągu znajdują się już jakieś dosłowne znaki spoza ASCII, coś pójdzie nie tak.unicode_escape
jest zasadniczo zaprojektowany do konwersji bajtów na tekst Unicode. Ale w wielu miejscach - na przykład w kodzie źródłowym Pythona - dane źródłowe są już tekstem Unicode.Jedynym sposobem, w jaki może to działać poprawnie, jest zakodowanie tekstu w bajtach. UTF-8 to rozsądne kodowanie całego tekstu, więc to powinno działać, prawda?
Poniższe przykłady są w Pythonie 3, więc literały ciągów są czystsze, ale ten sam problem występuje z nieco innymi manifestacjami w obu Pythonie 2 i 3.
Cóż, to źle.
Nowym zalecanym sposobem używania kodeków, które dekodują tekst na tekst, jest
codecs.decode
bezpośrednie wywołanie . To pomaga?Ani trochę. (Powyższe to również błąd UnicodeError w Pythonie 2.)
unicode_escape
Kodek, pomimo swojej nazwy, okazuje się założyć, że wszystkie bajty są non-ASCII w kodowaniu Latin-1 (ISO-8859-1). Więc musiałbyś to zrobić w ten sposób:Ale to straszne. To ogranicza cię do 256 znaków Latin-1, tak jakby Unicode nigdy nie został wynaleziony!
Dodanie wyrażenia regularnego w celu rozwiązania problemu
(Co zaskakujące, nie mamy teraz dwóch problemów.)
To, co musimy zrobić, to zastosować
unicode_escape
dekoder tylko do rzeczy, które na pewno są tekstem ASCII. W szczególności możemy upewnić się, że zastosujemy go tylko do prawidłowych sekwencji ucieczki Pythona, które na pewno będą tekstem ASCII.Plan jest taki, że znajdziemy sekwencje specjalne przy użyciu wyrażenia regularnego i użyjemy funkcji jako argumentu,
re.sub
aby zastąpić je wartością bez zmiany znaczenia.I z tym:
źródło
os.sep
w ogóle działa ? Próbuję to zrobić:patt = '^' + self.prefix + os.sep ; name = sub(decode_escapes(patt), '', name)
i to nie działa. W miejscu nowej linii znajduje się średnik.os.sep
jest twoje ?) Jeśli masz sekwencje specjalne z odwrotnym ukośnikiem w nazwach katalogów Windows, sytuacja jest prawie nie do naprawienia.Właściwie poprawna i wygodna odpowiedź dla Pythona 3:
Szczegóły dotyczące
codecs.escape_decode
:codecs.escape_decode
jest dekoderem bajtów do bajtówcodecs.escape_decode
dekoduje sekwencje specjalne ascii, takie jak:b"\\n"
->b"\n"
,b"\\xce"
->b"\xce"
.codecs.escape_decode
nie przejmuje się ani nie musi wiedzieć o kodowaniu obiektu bajtowego, ale kodowanie bajtów ze ucieczką powinno być zgodne z kodowaniem reszty obiektu.Tło:
unicode_escape
to nieprawidłowe rozwiązanie dla python3. Dzieje się tak, ponieważunicode_escape
dekoduje bajty ze ucieczką, a następnie dekoduje bajty na łańcuch znaków Unicode, ale nie otrzymuje żadnych informacji dotyczących kodeka do użycia w drugiej operacji.codecs.escape_decode
z tej odpowiedzi "jak mogę .decode ('string-escape') w Python3?" . Zgodnie z tą odpowiedzią funkcja ta nie jest obecnie udokumentowana dla Pythona 3.źródło
\x
bajtów UTF-8. Ale ponieważ dekoduje bajty na bajty, nie dekoduje - i nie może - dekodować żadnych znaków ucieczki znaków Unicode spoza ASCII, takich jak znaki specjalne\u
.ast.literal_eval
Funkcja jest blisko, ale będzie oczekiwać, że łańcuch jest prawidłowo cytowany pierwszy.Oczywiście interpretacja znaków ucieczki z ukośnikiem odwrotnym w Pythonie zależy od tego, w jaki sposób ciąg jest cytowany (
""
vsr""
vsu""
, potrójne cudzysłowy itp.), Więc możesz chcieć zawinąć dane wejściowe użytkownika w odpowiednie cudzysłowy i przekazać doliteral_eval
. Zawinięcie go w cudzysłów zapobiegnie równieżliteral_eval
zwróceniu liczby, krotki, słownika itp.Sprawy mogą się jeszcze skomplikować, jeśli użytkownik wpisze niecytowane cudzysłowy typu, który zamierzasz zawijać wokół ciągu.
źródło
myString = "\"\ndoBadStuff()\n\""
,print(ast.literal_eval('"' + myString + '"'))
że próbuje uruchomić kod. Czym jestast.literal_eval
coś innego / bezpieczniejszego niżeval
?literal_eval
nigdy nie wykonuje kodu. W dokumentacji: „Można to wykorzystać do bezpiecznego oceniania ciągów znaków zawierających wyrażenia Pythona z niezaufanych źródeł bez konieczności samodzielnego analizowania wartości”.Jest to zły sposób, ale zadziałał, gdy próbowałem zinterpretować znaki ósemkowe ze zmianą znaczenia przekazane w argumencie łańcuchowym.
Warto wspomnieć, że istnieje różnica między eval i ast.literal_eval (eval jest znacznie bardziej niebezpieczny). Zobacz Używanie metody eval () w Pythonie vs. ast.literal_eval ()?
źródło
Poniższy kod powinien działać dla \ n musi być wyświetlany w ciągu znaków.
źródło
replace
nic nie robią), używa dziko przestarzałych interfejsów API (string
funkcje modułu tego rodzaju są przestarzałe od Pythona 2.0, zastąpionestr
metodami i zniknęły całkowicie w Pythonie 3) i tylko obsługuje konkretny przypadek zastępowania pojedynczego znaku nowej linii, a nie ogólnego przetwarzania zmiany znaczenia.