有时当我从文件或用户获取输入时,我会得到一个带有转义序列的字符串。我希望以 Python 处理转义序列的方式处理转义序列
有时当我从文件或用户那里获取输入时,我会得到一个带有转义序列的字符串。我希望以 Python 处理字符串文字中的转义序列的方式 .
例如,假设 myString
定义为:
>>> myString = "spam\\neggs"
>>> print(myString)
spam\neggs
我想要一个可以执行以下操作的函数(我将其称为 process
):
>>> print(process(myString))
spam
eggs
重要的是,该函数可以处理 Python 中的所有转义序列(在上面的链接中的表格中列出)。
Python 有函数可以做到这一点吗?
unicode_escape
一般情况下不起作用
事实证明 string_escape
或 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 字节都采用 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):
try:
return codecs.decode(match.group(0), 'unicode-escape')
except UnicodeDecodeError:
# In case we matched the wrong thing after a double-backslash
return match.group(0)
return ESCAPE_SEQUENCE_RE.sub(decode_match, s)
然后:
>>> print(decode_escapes('Ernő \\t Rubik'))
Ernő Rubik