Jak cofnąć ucieczkę z ciągu znaków uciekających od ukośnika odwrotnego?


101

Załóżmy, że mam ciąg będący wersją innego ciągu znaków, w której zastosowano znak ucieczki odwrotnym ukośnikiem. Czy w Pythonie jest łatwy sposób na usunięcie ciągu znaków? Mógłbym na przykład:

>>> escaped_str = '"Hello,\\nworld!"'
>>> raw_str = eval(escaped_str)
>>> print raw_str
Hello,
world!
>>> 

Jednak wiąże się to z przekazaniem (prawdopodobnie niezaufanego) ciągu do eval (), co stanowi zagrożenie bezpieczeństwa. Czy w standardowej bibliotece znajduje się funkcja, która pobiera ciąg i tworzy ciąg bez wpływu na bezpieczeństwo?

Odpowiedzi:


138
>>> print '"Hello,\\nworld!"'.decode('string_escape')
"Hello,
world!"

9
Czy jest coś, co jest kompatybilne z Pythonem 3?
thejinx0r


30
Zasadniczo dla Pythona3, którego chceszprint(b"Hello,\nworld!".decode('unicode_escape'))
ChristopheD,

3
W przypadku Pythona 3 użyjvalue.encode('utf-8').decode('unicode_escape')
Casey Kuball

8
OSTRZEŻENIE: value.encode('utf-8').decode('unicode_escape') psuje znaki spoza ASCII w ciągu . O ile nie gwarantuje się, że dane wejściowe zawierają tylko znaki ASCII, nie jest to prawidłowe rozwiązanie.
Alex Peters

35

Możesz użyć tego, ast.literal_evalktóry jest bezpieczny:

Bezpiecznie oceń węzeł wyrażenia lub ciąg znaków zawierający wyrażenie Pythona. Dostarczony ciąg lub węzeł może składać się tylko z następujących struktur literałów Pythona: łańcuchów, liczb, krotek, list, dykt, wartości logicznych i None. (KONIEC)

Lubię to:

>>> import ast
>>> escaped_str = '"Hello,\\nworld!"'
>>> print ast.literal_eval(escaped_str)
Hello,
world!

3
Umieszczenie w ciągu znaków średnika ze znakiem ucieczki powoduje przerwanie tego kodu. Zgłasza błąd składni „nieoczekiwany znak po znaku kontynuacji wiersza”
darksky

3
@darksky zauważyć, że astbiblioteka wymaga cytaty (albo "albo 'nawet """czy ''') wokół escaped_str, ponieważ jest rzeczywiście próbuje uruchomić go jako Pythona (wstrzyknięcie zapobiega smyczkowy) kod, ale zwiększa bezpieczeństwo
InQβ

@ no1xsyzy: Co już ma miejsce w przypadku PO; to jest prawidłowa odpowiedź, gdy strjest repra strlub bytesprzedmiotem, jak w przypadku PO; unicode-escapeodpowiedź kodek jest, gdy nie jest repr, ale jakaś inna forma uciekła tekście (nie w cudzysłowie jako część danych łańcuchowych siebie).
ShadowRanger

ze znakami utf-8 to nie zadziała. sprawdź ostatnią odpowiedź z pakietem kodów. to faktycznie działa.
rubmz,

FWIW Próbowałem przeanalizować fragment tekstu JSON, który uciekł przed zmianą, i nadal otrzymywałem ten błąd, [ERROR] TypeError: string indices must be integersa to rozwiązanie działało, aby go rozwiązać. Cofnij znakowanie ciągu, a następnie przeanalizuj go jako JSON.
cyber-mnich

20

Wszystkie podane odpowiedzi będą łamać się na ogólnych ciągach Unicode. O ile wiem, poniższe działa dla Python3 we wszystkich przypadkach:

from codecs import encode, decode
sample = u'mon€y\\nröcks'
result = decode(encode(sample, 'latin-1', 'backslashreplace'), 'unicode-escape')
print(result)

Jak zaznaczono w komentarzach, możesz również użyć literal_evalmetody z astmodułu w następujący sposób:

import ast
sample = u'mon€y\\nröcks'
print(ast.literal_eval(F'"{sample}"'))

Lub w ten sposób, gdy twój ciąg naprawdę zawiera literał ciągu (w tym cudzysłowy):

import ast
sample = u'"mon€y\\nröcks"'
print(ast.literal_eval(sample))

Jeśli jednak nie masz pewności, czy ciąg wejściowy używa podwójnych, czy pojedynczych cudzysłowów jako separatorów, lub jeśli w ogóle nie możesz założyć, że jest on prawidłowo zmieniony , literal_evalmoże to spowodować SyntaxError, że metoda encode / decode będzie nadal działać.


ast.literal_eval('"mon€y\\nröcks"') == "mon€y\nröcks"działa dobrze dla mnie z Pythonem 3.7.3
oldrinb

Dzięki za komentarz @oldrinb! Zredagowałem odpowiedź, aby to uwzględnić.
Jesko Hüttenhain

14

W Pythonie 3 strobiekty nie mają decodemetody i musisz użyć bytesobiektu. Odpowiedź ChristopheDa obejmuje Pythona 2.

# create a `bytes` object from a `str`
my_str = "Hello,\\nworld"
# (pick an encoding suitable for your str, e.g. 'latin1')
my_bytes = my_str.encode("utf-8")

# or directly
my_bytes = b"Hello,\\nworld"

print(my_bytes.decode("unicode_escape"))
# "Hello,
# world"

2
Wprowadzenie go razem value.encode('utf-8').decode('unicode_escape').
Casey Kuball

6
To niestety się
zepsuje,

Czy próbowałeś wybrać kodowanie odpowiednie do polerowania w wywołaniu encode?
asac

ze znakami utf-8 to nie zadziała. sprawdź ostatnią odpowiedź z pakietem kodów. to faktycznie działa.
rubmz
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.