在Python中处理string中的转义序列

有时候,当我从文件或用户获得input,我得到一个string中的转义序列。 我想以Python处理string文字转义序列的方式来处理转义序列 。

例如,假设myString被定义为:

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

我想要一个函数(我会叫它的process ),这样做:

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

函数能够处理Python中的所有转义序列(在上面的链接中的表格中列出)是非常重要的。

Python有一个function来做到这一点?

正确的做法是使用“string转义”代码来解码string。

 >>> 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或评估。 使用string编解码器更安全。

unicode_escape一般不起作用

事实certificate, string_escapeunicode_escape解决scheme通常不起作用,特别是在实际的Unicode存在的情况下不起作用。

如果你能确定每一个非ASCII字符都会被转义(记住,除了前128个字符以外的任何东西都是非ASCII的), unicode_escape会为你做正确的事情。 但是,如果string中已经有字符的非ASCII字符,事情就会出错。

unicode_escape从根本上被devise成将字节转换为Unicode文本。 但是在许多地方 – 例如Python源代码 – 源数据已经是Unicode文本。

这可以正确工作的唯一方法是如果您先将文本编码为字节。 UTF-8是所有文本的合理编码,所以应该工作,对不对?

下面的例子是在Python 3中,所以string文字更清晰,但是同样的问题存在,Python 2和Python 3的performance略有不同。

 >>> 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编解码器,尽pipe它的名字,原来假设所有非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) 

添加正则expression式来解决问题

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

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

计划是,我们将使用正则expression式来查找转义序列,并使用一个函数作为re.sub的参数,用它们的非转义值replace它们。

 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 

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不正确的解决scheme。 这是因为unicode_escape解码转义字节,然后将字节解码为unicodestring,但没有收到有关第二个操作使用哪个编解码器的信息。
  • @Jubub是正确的:避免AST或评估。
  • 我首先从这个答案中发现了codecs.escape_decode ,“我如何在Python3中parsing('string-escape')?” 。 正如答案所述,该function目前没有为python 3logging。

ast.literal_eval函数接近,但会期望string被正确引用。

当然,Python对反斜杠转义的解释取决于string是如何引用的( "" vs“ r"" vs u"" ,三重引号等),所以你可能想把用户input包装在合适的引号中并传递给literal_eval 。 用引号括起来也会阻止literal_eval返回数字,元组,字典等。

如果用户input你打算围绕string的types的不加引号的引号,情况可能会变得棘手。

如果你信任数据的来源,只需要引用它和eval()呢?

 >>> myString = 'spam\\neggs' >>> print eval('"' + myString.replace('"','') + '"') spam eggs 

PS。 添加邪恶代码exec对策 – 现在它将剥离所有"在评估之前