问题:在Python中处理字符串中的转义序列
有时,当我从文件或用户那里得到输入时,我会得到一个带有转义序列的字符串。我想以与Python处理字符串文字中的转义序列相同的方式来处理转义序列。
例如,假设myString
定义为:
>>> myString = "spam\\neggs"
>>> print(myString)
spam\neggs
我想要一个process
执行此操作的函数(我称之为):
>>> print(process(myString))
spam
eggs
该函数可以处理Python中的所有转义序列(在上面的链接的表格中列出),这一点很重要。
Python是否具有执行此操作的功能?
回答 0
正确的做法是使用“字符串转义”代码对字符串进行解码。
>>> 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
unicode_escape
总的来说不起作用
事实证明,string_escape
or 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
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中记录。
回答 3
该ast.literal_eval
函数将关闭,但是它将期望该字符串先被正确引用。
当然反斜杠Python的解释依赖于字符串的方式引用(""
VS r""
VS u""
,三引号等),所以你可能想包装在合适的报价的用户输入和传递给literal_eval
。将其包装在引号中还可以防止literal_eval
返回数字,元组,字典等。
如果用户键入您打算在字符串周围使用的引号引起来,事情可能仍然会变得棘手。
回答 4
这是一个不好的方法,但是当我尝试解释在字符串参数中传递的转义八进制时,它对我有用。
input_string = eval('b"' + sys.argv[1] + '"')
值得一提的是,eval和ast.literal_eval之间存在区别(eval更加不安全)。请参阅使用python的eval()与ast.literal_eval()吗?
回答 5
下面的代码应该适用于\ n,要求将其显示在字符串上。
import string
our_str = 'The String is \\n, \\n and \\n!'
new_str = string.replace(our_str, '/\\n', '/\n', 1)
print(new_str)