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

时间:2010-10-26 03:43:57

标签: python string escaping

有时当我从文件或用户那里获得输入时,我会得到一个包含转义序列的字符串。我想处理转义序列in the same way that Python processes escape sequences in string literals

例如,假设myString定义为:

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

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

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

该函数可以处理Python中的所有转义序列(在上面链接的表中列出)非常重要。

Python有功能吗?

7 个答案:

答案 0 :(得分:113)

正确的做法是使用'string-escape'代码解码字符串。

>>> 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。使用字符串编解码器更加安全。

答案 1 :(得分:96)

unicode_escape一般不起作用

事实证明,string_escapeunicode_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字节都采用Latin-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 :(得分:20)

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字符串,但没有收到有关用于第二次操作的编解码器的信息。
  • @Jerub是正确的:避免使用AST或eval。
  • 我首先从this answer to "how do I .decode('string-escape') in Python3?"发现了codecs.escape_decode。正如该答案所述,目前没有为python 3记录该功能。

答案 3 :(得分:6)

ast.literal_eval函数接近,但它会期望首先正确引用字符串。

当然,Python对反斜杠转义的解释取决于引用字符串的方式("" vs r"" vs u"",三引号等等,所以你可能想要包装用户输入在适当的引号中传递给literal_eval。用引号括起来也会阻止literal_eval返回数字,元组,字典等。

如果用户输入要打包字符串的类型的不带引号的引号,那么事情仍然会变得棘手。

答案 4 :(得分:1)

这是一个不好的方法,但是当我尝试解释在字符串参数中传递的转义八进制时,它对我有用。

input_string = eval('b"' + sys.argv[1] + '"')

值得一提的是,eval和ast.literal_eval之间存在区别(eval更加不安全)。参见Using python's eval() vs. ast.literal_eval()?

答案 5 :(得分: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)

答案 6 :(得分:0)

(目前)Jerub接受的答案对于python2是正确的,但不正确,并且可能会为python3产生乱码(如Apalala在对该解决方案的评论中指出)。这是因为unicode_escape编解码器要求其源代码按照官方python docs使用latin-1(而不是utf-8)进行编码。因此,在python3中使用:

>>> myString="špåm\\nëðþ\\x73"
>>> print(myString)
špåm\nëðþ\x73
>>> decoded_string = myString.encode('latin-1','backslashreplace').decode('unicode_escape')
>>> print(decoded_string)
špåm
ëðþs

此方法还避免了metatoaster对Jerub解决方案的注释中的字符串和字节之间的多余不必要往返(但对于metatoaster可以识别该解决方案中的错误表示敬意)。