有时当我从文件或用户那里获得输入时,我会得到一个包含转义序列的字符串。我想处理转义序列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有功能吗?
答案 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_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):
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
不关心或不需要知道字节对象的编码,但转义字节的编码应该与对象其余部分的编码匹配。背景:
unicode_escape
是python3的错误解决方案。这是因为unicode_escape
解码了转义字节,然后将字节解码为unicode字符串,但没有收到有关用于第二次操作的编解码器的信息。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可以识别该解决方案中的错误表示敬意)。