如何取消转义反斜杠的字符串?

135

假设我有一个字符串,其中包含另一个字符串的反斜杠转义版本。在Python中,有没有一种简单的方法来取消转义字符串?例如,我可以这样做:

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

然而,这涉及将可能不受信任的字符串传递给eval(),这是一种安全风险。标准库中是否有一个函数,可以接受一个字符串,并生成一个没有安全影响的字符串?


1
如果你需要取消转义一个特定的单个字符(比如 '\n'),你可以使用s.replace('\\n', '\n')。我没有发布回答是因为问题更为普遍,但我有类似的问题,并且不想用字节和编码来复杂化自己,所以把这个放在这里供其他人参考... - Tomerikoo
6个回答

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

24
有什么与Python 3兼容的东西吗? - thejinx0r
6
@thejinx0r:请看这里:https://dev59.com/wGUq5IYBdhLWcg3wEcRZ - ChristopheD
43
基本上,对于Python3,您希望使用print(b"Hello,\nworld!".decode('unicode_escape'))进行输出。 - ChristopheD
10
对于Python 3,请使用value.encode('utf-8').decode('unicode_escape')来进行转码。 - Casey Kuball
19
警告:value.encode('utf-8').decode('unicode_escape') 会破坏字符串中的非ASCII字符。除非输入保证只包含ASCII字符,否则这不是一个有效的解决方案。 - Alex Peters
显示剩余6条评论

50

你可以使用ast.literal_eval这个函数,它是安全的:

安全地评估表达式节点或包含Python表达式的字符串。提供的字符串或节点只能由以下Python字面结构组成:字符串,数字,元组,列表,字典,布尔值和None。(结束)

像这样:

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

3
字符串中出现分号未被转义,导致代码出错。会抛出一个语法错误:"行继续字符后面有意外的字符"。 - darksky
3
请注意,'ast'库需要在转义的字符串周围加上引号(可以是"',甚至是"""'''),因为它实际上正在尝试将其作为Python代码运行,但增强了安全性(防止字符串注入)。 - InQβ
@no1xsyzy:在OP的情况下已经是这种情况了;当strreprstrbytes对象时,这是正确的答案,就像在OP的情况下一样;当它不是repr,而是其他形式的转义文本(不作为字符串数据本身的一部分用引号括起来)时,unicode-escape编解码器的答案才适用。 - ShadowRanger
请检查最后一个答案中的代码包,这实际上是有效的。 - rubmz
顺便说一下,我试图解析一些转义的JSON文本,但一直出现错误[ERROR] TypeError: string indices must be integers,这个解决方案可以解决这个问题。先取消字符串转义,然后再解析为JSON。 - cyber-monk
如果字符串包含正斜杠,则会抛出SyntaxError。 - Elliott B

47

所有给出的答案都无法处理一般的Unicode字符串。以下内容适用于Python3中的所有情况,据我所知:

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

在最近的Python版本中,这也可以在不导入任何模块的情况下工作:

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

obataku所建议的,你也可以使用ast模块中的literal_eval方法,操作如下:

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

当你的字符串确实包含一个字符串字面量(包括引号)时,可以这样写:

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

然而,如果您不确定输入字符串使用双引号还是单引号作为分隔符,或者无法假定它完全被正确转义,则literal_eval可能会引发SyntaxError,而编码/解码方法仍将正常工作。

我不相信这个处理所有转义的UTF-8字符串都是正确的。例如,以s ='\\ xe7 \\ xa6 \\ x98'开头,Python2中的print s.decode('string-escape')会像我希望的那样打印,但是您在Python3中的答案会打印ç§。另一个相关问题的此答案在Python3中似乎做到了我所期望的:print(s.encode('latin-1').decode('unicode_escape').encode('latin-1').decode('utf-8')) - James
嘿@James,对于你的问题,没有通用解决方案也适用于“正确”的编码,因为无法知道它是什么。在你的例子中,你期望UTF-8,但如果你期望CP1252,那么你的代码显然会失败。然而 - 如果你将我的代码应用于字符串s='\\u79d8',你将得到你要找的字符!不同之处在于你的输入是其utf8编码的转义版本,但输入s='\\u79d8'是字符串的转义版本。 - Jesko Hüttenhain

20
在Python 3中,str 对象没有decode方法,你需要使用一个bytes对象。ChristopheD的回答涵盖了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"

3
将它们放在一起,value.encode('utf-8').decode('unicode_escape') - Casey Kuball
11
如果字符串包含一些UTF-8非ASCII字符(例如波兰字符),这将很遗憾地出错。 - Pax0r
你尝试在调用 encode 时选择适合波兰语的编码了吗? - asachet
请检查最后一个答案中的代码包,这实际上是有效的。 - rubmz

19

对于Python3,请考虑:

my_string.encode('raw_unicode_escape').decode('unicode_escape')

'raw_unicode_escape' 编解码器将编码为 latin1,但首先用转义形式的 '\uXXXX''\UXXXXXXXX' 替换所有其他 Unicode 代码点。重要的是,它与普通的 'unicode_escape' 编解码器不同,在于它不会触及现有的反斜杠。

因此,当应用正常的 'unicode_escape' 解码器时,新转义的代码点和最初转义的元素被等同对待,结果是一个未经转义的本地 Unicode 字符串。

('raw_unicode_escape' 解码器似乎只关注 '\uXXXX''\UXXXXXXXX' 形式,忽略所有其他转义字符。)

文档: https://docs.python.org/3/library/codecs.html?highlight=codecs#text-encodings


那么我猜测s.encode('latin-1', 'backslashreplace')s.encode('raw_unicode_escape')是相同的,我是对的吧?还是有某些微妙的区别使得在这种特定应用中使用'raw_unicode_escape'更好? - Donovan Baarda

0

自定义字符串解析器,仅解码某些反斜杠转义字符,例如 \"\'

def backslash_decode(src):
    "decode backslash-escapes"
    slashes = 0 # count backslashes
    dst = ""
    for loc in range(0, len(src)):
        char = src[loc]
        if char == "\\":
            slashes += 1
            if slashes == 2:
                dst += char # decode backslash
                slashes = 0
        elif slashes == 0:
            dst += char # normal char
        else: # slashes == 1
            if char == '"':
                dst += char # decode double-quote
            elif char == "'":
                dst += char # decode single-quote
            else:
                dst += "\\" + char # keep backslash-escapes like \n or \t
            slashes = 0
    return dst

src = "a" + "\\\\" + r"\'" + r'\"' + r"\n" + r"\t" + r"\x" + "z" # input
exp = "a" + "\\"   +  "'"  +  '"'  + r"\n" + r"\t" + r"\x" + "z" # expected output

res = backslash_decode(src)

print(res)
assert res == exp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接