在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_escape
或unicode_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对策 – 现在它将剥离所有"
在评估之前