Como cancelar o escape de uma string com escape de barra invertida?

100

Suponha que eu tenha uma string que é uma versão com escape de barra invertida de outra string. Existe uma maneira fácil, em Python, de desfazer a fuga da string? Eu poderia, por exemplo, fazer:

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

No entanto, isso envolve passar uma string (possivelmente não confiável) para eval (), o que é um risco de segurança. Existe uma função no lib padrão que pega uma string e produz uma string sem implicações de segurança?

usuario
fonte

Respostas:

137
>>> print '"Hello,\\nworld!"'.decode('string_escape')
"Hello,
world!"
ChristopheD
fonte
9
Existe algo que seja compatível com o python 3?
thejinx0r
3
@ thejinx0r: dê uma olhada aqui: stackoverflow.com/questions/14820429/…
ChristopheD
29
Basicamente para Python3 que você desejaprint(b"Hello,\nworld!".decode('unicode_escape'))
ChristopheD,
3
Para python 3, usevalue.encode('utf-8').decode('unicode_escape')
Casey Kuball
8
AVISO: value.encode('utf-8').decode('unicode_escape') corrompe caracteres não ASCII na string . A menos que seja garantido que a entrada contenha apenas caracteres ASCII, esta não é uma solução válida.
Alex Peters
34

Você pode usar o ast.literal_evalque é seguro:

Avalie com segurança um nó de expressão ou uma string contendo uma expressão Python. A string ou nó fornecido pode consistir apenas nas seguintes estruturas literais do Python: strings, números, tuplas, listas, dicts, booleanos e nenhum. (FIM)

Como isso:

>>> import ast
>>> escaped_str = '"Hello,\\nworld!"'
>>> print ast.literal_eval(escaped_str)
Hello,
world!
jatanismo
fonte
3
Ter um ponto-e-vírgula de escape na string quebra esse código. Lança um erro de sintaxe "caractere inesperado após caractere de continuação de linha"
darksky 01 de
3
@darksky observe que a astbiblioteca requer aspas ( "ou ', mesmo """ou ''') em torno de seu escaped_str, uma vez que está realmente tentando executá-lo como código Python, mas aumenta a segurança (evita injeção de string)
InQβ
@ no1xsyzy: O que no caso do OP já é o caso; esta é a resposta correta quando o stré a reprde a strou um bytesobjeto como no caso do OP; a unicode-escaperesposta do codec é para quando não é um repr, mas alguma outra forma de texto de escape (não entre aspas como parte dos próprios dados da string).
ShadowRanger
com caracteres utf-8 isso não funcionará. verifique a última resposta com pacote de códigos. realmente funciona.
rubmz
20

Todas as respostas fornecidas serão interrompidas em strings Unicode gerais. O seguinte funciona para Python3 em todos os casos, tanto quanto posso dizer:

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

Conforme descrito nos comentários, você também pode usar o literal_evalmétodo do astmódulo assim:

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

Ou assim, quando sua string realmente contém um literal de string (incluindo as aspas):

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

No entanto, se você não tiver certeza se a string de entrada usa aspas duplas ou simples como delimitadores, ou quando você não pode presumir que ela tenha um escape adequado, literal_evalpode demorar um SyntaxErrorpouco para que o método de codificação / decodificação ainda funcione.

Jesko Hüttenhain
fonte
ast.literal_eval('"mon€y\\nröcks"') == "mon€y\nröcks"funciona bem para mim com Python 3.7.3
oldrinb
Obrigado pelo comentário @oldrinb! Eu editei a resposta para incluir isso.
Jesko Hüttenhain
14

No python 3, os strobjetos não têm um decodemétodo e você deve usar um bytesobjeto. A resposta de ChristopheD cobre python 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"
asac
fonte
2
Juntando tudo value.encode('utf-8').decode('unicode_escape'),.
Casey Kuball
6
Infelizmente, isso será interrompido se a string contiver alguns caracteres não-ascii utf-8 (ou seja, caracteres
polidos
Você já tentou escolher uma codificação adequada para polimento na chamada para encode?
asac
com caracteres utf-8 isso não funcionará. verifique a última resposta com pacote de códigos. realmente funciona.
rubmz