在Python中处理字符串中的转义序列


112

有时,当我从文件或用户那里得到输入时,我会得到一个带有转义序列的字符串。我想以与Python处理字符串文字中的转义序列相同的方式来处理转义序列

例如,假设myString定义为:

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

我想要一个process执行此操作的函数(我称之为):

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

该函数可以处理Python中的所有转义序列(在上面的链接的表格中列出),这一点很重要。

Python是否具有执行此操作的功能?


1
hmmm,您希望包含字符串的'spam'+"eggs"+'''some'''+"""more"""处理有多精确?
Nas Banov

@Nas Banov这是一个很好的测试。该字符串不包含转义序列,因此在处理后应该完全相同。myString = "'spam'+\"eggs\"+'''some'''+\"\"\"more\"\"\""print(bytes(myString, "utf-8").decode("unicode_escape"))似乎有效。
dln385 2010年

5
这个问题的大多数答案都有严重的问题。在不破坏unicode的情况下,似乎没有标准的方法可以在Python中使用转义序列。@rspeer发布的答案是我为Grako所采用的答案,因为到目前为止它可以处理所有已知案例。
阿巴拉拉

Answers:


137

正确的做法是使用“字符串转义”代码对字符串进行解码。

>>> 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

不要使用AST或eval。使用字符串编解码器更加安全。


3
放手,最好的解决方案!顺便说一句,由文档,它应该是“ string_escape”(带下划线),但由于某种原因,可以接受“ stringscape”,“ string @ escape”等形式的任何内容……基本上'string\W+escape'
Nas Banov

2
@Nas Banov该文档的确没有提及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.
dln385 2010年

29
该解决方案还不够好,因为它无法处理原始字符串中包含合法unicode字符的情况。如果您尝试: >>> print("juancarlo\\tañez".encode('utf-8').decode('unicode_escape')) 您得到: juancarlo añez
Apalala 2014年

2
同意@Apalala:这还不够好。在下面查看rseeper的答案,以获取适用于Python2和3的完整解决方案!
Christian Aichinger

2
由于latin1由假设unicode_escape,请重做编码/解码位,例如s.encode('utf-8').decode('unicode_escape').encode('latin1').decode('utf8')
metatoaster,

121

unicode_escape 总的来说不起作用

事实证明,string_escapeor unicode_escape解决方案通常无法正常工作-尤其是在存在实际Unicode的情况下,它不能正常工作。

如果您可以确定每个非ASCII字符都会被转义(并且请记住,前128个字符以外的任何字符都是非ASCII),unicode_escape将为您做正确的事。但是,如果您的字符串中已经有任何文字上的非ASCII字符,则会出错。

unicode_escape从根本上来说是设计用来将字节转换为Unicode文本。但是在许多地方(例如Python源代码),源数据已经是Unicode文本。

唯一可以正常工作的方法是首先将文本编码为字节。UTF-8是所有文本的明智编码,因此应该可以使用,对吧?

以下示例是Python 3中的示例,因此字符串文字更清晰,但在Python 2和3上,存在相同的问题,但表现形式略有不同。

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

好吧,那是错误的。

建议使用编解码器将文本解码为文本的新方法是codecs.decode直接调用。有帮助吗?

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

一点也不。(此外,以上是Python 2上的UnicodeError。)

unicode_escape编解码器,尽管它的名字,原来假设所有非ASCII字节拉丁-1(ISO-8859-1)编码。因此,您必须这样做:

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

但这太可怕了。这将您限制为256个Latin-1字符,就好像根本没有发明Unicode一样!

>>> 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)

添加正则表达式以解决问题

(令人惊讶的是,我们现在没有两个问题。)

我们需要做的只是将unicode_escape解码器应用于我们确定为ASCII文本的内容。特别是,我们可以确保仅将其应用于有效的Python转义序列,这些序列必须保证为ASCII文本。

计划是,我们将使用正则表达式查找转义序列,并使用函数作为参数以re.sub将其替换为未转义的值。

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)

然后:

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

2
我们需要更多类似的答案。谢谢。
v.oddou 2015年

这一点有用os.sep吗?我正在尝试执行此操作:patt = '^' + self.prefix + os.sep ; name = sub(decode_escapes(patt), '', name)并且它不起作用。分号代替了新的一行。
Pureferret

@Pureferret我不太确定您要问的是什么,但您可能不应该在反斜杠具有不同含义的字符串上运行此命令,例如Windows文件路径。(这就是您os.sep的意思吗?)如果您在Windows目录名称中使用了反斜杠转义序列,则这种情况几乎无法恢复。
rspeer 2015年

转义序列不具有在他们逃脱,但我发现了一个“假转义字符串”的错误
Pureferret

这告诉我,你结束了一些其他的正则表达式用反斜杠:stackoverflow.com/questions/4427174/...
rspeer

33

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

有关的详细信息codecs.escape_decode

  • codecs.escape_decode 是一个逐字节解码器
  • codecs.escape_decode解码ascii转义序列,例如:b"\\n"-> b"\n"b"\\xce"-> b"\xce"
  • codecs.escape_decode 不需要或不需要了解字节对象的编码,但是转义字节的编码应与对象其余部分的编码匹配。

背景:

  • @rspeer是正确的:unicode_escape是python3的错误解决方案。这是因为先unicode_escape解码转义的字节,然后再将字节解码为unicode字符串,但没有收到有关第二个操作使用哪个编解码器的信息。
  • @耶鲁布是正确的:避免AST或eval。
  • 我首先codecs.escape_decode这个答案中发现“我如何在Python3中解码('string-escape')?” 。如该答案所述,该功能目前尚未在python 3中记录。

这是真正的答案(:太糟糕了,它依赖于文档记录不良的功能
。– jwd

5
这是针对您的转义序列\x为UTF-8字节转义的情况的答案。但是,因为它将字节解码为字节,所以它不会(也不能)解码任何非ASCII Unicode字符的\u转义符,例如转义符。
rspeer

仅供参考,此功能在技术上不公开。参见bugs.python.org/issue30588
Hack5

8

ast.literal_eval函数将关闭,但是它将期望该字符串先被正确引用。

当然反斜杠Python的解释依赖于字符串的方式引用(""VS r""VS u"",三引号等),所以你可能想包装在合适的报价的用户输入和传递给literal_eval。将其包装在引号中还可以防止literal_eval返回数字,元组,字典等。

如果用户键入您打算在字符串周围使用的引号引起来,事情可能仍然会变得棘手。


我懂了。正如您所说的那样myString = "\"\ndoBadStuff()\n\"",这似乎具有潜在的危险:print(ast.literal_eval('"' + myString + '"'))似乎试图运行代码。有ast.literal_eval什么不同之处/更安全eval
dln385 2010年

5
@ dln385:literal_eval从不执行代码。在文档中,“这可用于安全地评估包含来自不受信任来源的Python表达式的字符串,而无需自己解析值。”
Greg Hewgill


0

下面的代码应该适用于\ n,要求将其显示在字符串上。

import string

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

1
这不能按书面要求工作(正斜杠replace什么也没做),使用过时的API(从stringPython 2.0开始不推荐使用此类模块功能,由str方法取代,并在Python 3中完全消失),并且仅处理替换单个换行符的特定情况,而不是常规转义处理。
ShadowRanger
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.