例如,假设
myString
被定义为:>>> myString = "spam\\neggs"
>>> print(myString)
spam\neggs
我希望有一个函数(我将其称为process
),它可以执行以下操作:
>>> print(process(myString))
spam
eggs
重要的是该函数能够处理Python中的所有转义序列(在上面链接的表格中列出)。
Python是否有此功能的函数?
myString
被定义为:>>> myString = "spam\\neggs"
>>> print(myString)
spam\neggs
我希望有一个函数(我将其称为process
),它可以执行以下操作:
>>> print(process(myString))
spam
eggs
重要的是该函数能够处理Python中的所有转义序列(在上面链接的表格中列出)。
Python是否有此功能的函数?
正确的做法是使用'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。使用字符串编解码器更安全。
'string\W+escape'
。 - Nas Banov>>> print("juancarlo\\tañez".encode('utf-8').decode('unicode_escape'))
你会得到:juancarlo añez
- Apalalaunicode_escape
假定为 latin1
,因此需要重新进行编码/解码处理,例如 s.encode('utf-8').decode('unicode_escape').encode('latin1').decode('utf8')
。 - metatoasterutf-8
表示法可以将Unicode码点大于127的字符转换为bytes
并落在ascii
范围内(0-127),因为所有多字节字符都在128-255的范围内(即0x80
- 0xff
),这是因为Unicode和UTF-8的设计者理解了这个确切问题。换句话说,不,使用str.encode('utf-8')
除了Unicode码点U+005C
之外,不可能产生bytes
b'\x5c'
(0x5c
)。 - metatoasterunicode_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
>>> print(s.encode('latin-1').decode('unicode_escape'))
naïve test
>>> 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
os.sep
吗?)如果你的Windows目录名称中有反斜杠转义序列,那么情况几乎无法挽回。 - rspeerunicode-escape
不能正确处理:test = "\\xe2\\x80\\xa6"
test_bytes = test.encode()
test = test_bytes.decode("unicode-escape")
值:
test_bytes
== b'\\xe2\\x80\\xa6'
test
== 'â¦'
- Mark Ingram\xe2
实际上意味着"unicode字符E2"而不是"字节E2"。它与字节无关。如果您能够使其尝试匹配字节字符串,则必须更改了代码或使用了Python 2强制转换。 - rspeer对于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
是错误的解决方案。这是因为 unicode_escape
先将转义的字节解码,然后将字节解码为 Unicode 字符串,但对于第二个操作,它没有接收到有关使用哪个编解码器的信息。codecs.escape_decode
,该回答是针对“如何在 Python3 中进行 .decode('string-escape')”这个问题的。正如该回答所述,该函数目前未在 Python 3 中记录。\x
转义序列是 UTF-8 字节的情况下的答案。但由于它将字节解码为字节,所以它不会也不能解码任何非 ASCII Unicode 字符的转义序列,比如 \u
转义序列。 - rspeerast.literal_eval
函数接近需求,但它要求字符串被正确引用。
当然,Python对反斜杠转义的解释取决于字符串如何被引用(使用双引号""
、原始字符串r""
、Unicode字符串u""
、三重引号等),所以您可能需要将用户输入的内容包装在适当的引号中,再传递给literal_eval
函数。用引号包装还可以防止literal_eval
返回数字、元组、字典等。
如果用户键入了未经引用的引号,则仍可能会变得棘手。
myString = "\"\ndoBadStuff()\n\""
, print(ast.literal_eval('"' + myString + '"'))
似乎尝试运行代码。那么 ast.literal_eval
和 eval
有什么不同/更安全的地方呢? - dln385literal_eval
永远不会执行代码。根据文档,“这可用于安全地评估来自不受信任来源的包含 Python 表达式的字符串,而无需自己解析值。” - Greg Hewgill目前,Jerub的答案对于python2是正确的,但对于python3则可能产生乱码的结果(正如Apalala在评论中指出的那样)。这是因为unicode_escape编解码器要求其源代码使用latin-1进行编码,而不是utf-8,根据官方的Python文档。因此,在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
.decode
步骤尝试替换的确切格式。因此,例如,myString ='日本\u8a9e'
可以正确地给出日本語
。但是,它无法处理我答案中描述的真正恶劣的情况。 - Karl Knechtel正确引用字符串,使其看起来像等效的Python字符串文字,并使用ast.literal_eval
。这是安全的,但比您想象的要棘手得多。
添加"
到字符串的开头和结尾很容易,但我们还需要确保字符串中的任何"
都被正确转义。如果我们想要完全符合Python的翻译,我们需要考虑无效转义序列的已弃用行为。
事实证明,我们需要在以下情况下添加一个反斜杠:
任何一组偶数个反斜杠后跟一个双引号的序列(以便在需要时转义引号,但不转义反斜杠并取消转义引号,如果它已经被转义);以及
输入末尾的奇数个反斜杠的序列(否则反斜杠将转义我们的封闭双引号)。
这里是一个酸性测试输入,显示了许多困难的情况:
>>> text = r'''\\ \ \" \\" \\\" \'你好'\n\u062a\xff\N{LATIN SMALL LETTER A}"''' + '\\'
>>> text
'\\\\ \\ \\" \\\\" \\\\\\" \\\'你好\'\\n\\u062a\\xff\\N{LATIN SMALL LETTER A}"\\'
>>> print(text)
\\ \ \" \\" \\\" \'你好'\n\u062a\xff\N{LATIN SMALL LETTER A}"\
我最终能够编写出一个正则表达式,可以正确处理所有这些情况,从而允许使用literal_eval
:
>>> def parse_escapes(text):
... fixed_escapes = re.sub(r'(?<!\\)(\\\\)*("|\\$)', r'\\\1\2', text)
... return ast.literal_eval(f'"{fixed_escapes}"')
...
测试结果:
>>> parse_escapes(text)
'\\ \\ " \\" \\" \'你好\'\nتÿa"\\'
>>> print(parse_escapes(text))
\ \ " \" \" '你好'
تÿa"\
这应该正确处理所有情况 - 包含单引号和双引号的字符串,所有带反斜杠的奇怪情况以及输入中的非ASCII字符。(我承认用肉眼验证结果有点困难!)
这种做法并不好,但当我尝试解释传递给字符串参数的转义八进制数时,它对我起了作用。
input_string = eval('b"' + sys.argv[1] + '"')
eval
。这会允许提供该输入的用户在您的计算机上运行任意代码。这并不是非常简单的沙盒操作。 - Karl Knechtelimport string
our_str = 'The String is \\n, \\n and \\n!'
new_str = string.replace(our_str, '/\\n', '/\n', 1)
print(new_str)
replace
函数无效),使用的API已经过时(此类的“string”模块函数自Python 2.0起被弃用,被“str”方法替代,并在Python 3中完全消失),并且仅处理替换单个换行符的特定情况,而不是一般的转义处理。 - ShadowRanger
'spam'+"eggs"+'''some'''+"""more"""
的字符串会被如何处理呢? - Nas Banov