在Python中处理字符串中的转义序列

dln*_*385 93 python string escaping

有时当我从文件或用户那里获得输入时,我会得到一个包含转义序列的字符串.我想以与Python处理字符串文字中的转义序列相同的方式处理转义序列.

例如,假设myString定义为:

>>> myString = "spam\\neggs"
>>> print(myString)
spam\neggs
Run Code Online (Sandbox Code Playgroud)

我想要一个函数(我会称之为process)这样做:

>>> print(process(myString))
spam
eggs
Run Code Online (Sandbox Code Playgroud)

重要的是该函数可以处理Python中的所有转义序列(在上面链接的表中列出).

Python有功能吗?

Jer*_*rub 122

正确的做法是使用'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
Run Code Online (Sandbox Code Playgroud)

不要使用AST或eval.使用字符串编解码器更安全.

  • 这个解决方案不够好,因为它不能处理原始字符串中存在合法unicode字符的情况.如果你尝试:``>>> print("juancarlo \\tañez".encode('utf-8').decode('unicode_escape'))``你得到:``juancarloañez`` (26认同)
  • 亲自动手,**最佳**解决方案!顺便说一句,文档应该是"string_escape"(带下划线),但由于某种原因接受模式'string escape'中的任何内容,'string @ escape'等等...基本上''字符串\ W + escape'` (3认同)
  • @Nas Banov文档确实[稍微提及一下](http://docs.python.org/library/codecs.html#standard-encodings):"请注意,拼写替代方案仅在案例中有所不同或使用连字符而不是下划线也是有效的别名; 因此,例如'utf-8'是'utf_8'编解码器的有效别名 (2认同)
  • 同意@Apalala:这还不够好.查看下面的rseeper的答案,获得适用于Python2和3的完整解决方案! (2认同)
  • 由于`latin1`由`unicode_escape`假定,重做编码/解码位,例如`s.encode('utf-8').decode('unicode_escape').encode('latin1').decode('utf8' )` (2认同)
  • @DonovanBaarda不,没有任何unicode代码点> 127的多字节“utf-8”表示在“ascii”范围(0-127)内产生“字节”,因为所有多字节字符都在128范围内-255(即 `0x80` - `0xff`),因为 unicode 和 utf-8 的设计者理解这个确切的问题。换句话说,不,“str.encode('utf-8')”不可能从除 unicode 代码点“U”以外的任何内容生成“字节”“b”\x5c”(“0x5c”) +005C`。 (2认同)

rsp*_*eer 107

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
Run Code Online (Sandbox Code Playgroud)

嗯,那是错的.

使用将文本解码为文本的编解码器的新推荐方法是codecs.decode直接调用.这有帮助吗?

>>> import codecs
>>> print(codecs.decode(s, 'unicode_escape'))
naïve   test
Run Code Online (Sandbox Code Playgroud)

一点也不.(另外,上面是Python 2上的UnicodeError.)

unicode_escape编解码器,尽管它的名字,原来假设所有非ASCII字节拉丁-1(ISO-8859-1)编码.所以你必须这样做:

>>> print(s.encode('latin-1').decode('unicode_escape'))
naïve    test
Run Code Online (Sandbox Code Playgroud)

但那太可怕了.这限制你使用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)
Run Code Online (Sandbox Code Playgroud)

添加正则表达式来解决问题

(令人惊讶的是,我们现在没有两个问题.)

我们需要做的只是将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)
Run Code Online (Sandbox Code Playgroud)

随之而来的是:

>>> print(decode_escapes('Ern? \\t Rubik'))
Ern?     Rubik
Run Code Online (Sandbox Code Playgroud)

  • 我们需要更多这样的答案。谢谢。 (2认同)

use*_*087 27

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
Run Code Online (Sandbox Code Playgroud)

有关的详情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字符串,但不接收有关用于第二个操作的编解码器的信息.
  • @Jerub是正确的:避免AST或eval.
  • 我首先codecs.escape_decode这个答案中发现了"我如何在Python3中解码('string-escape')?" .正如该答案所述,目前没有为python 3记录该函数.

  • 这是你的转义序列是`\ x`转义为UTF-8字节的情况的答案.但是因为它将字节解码为字节,所以它不会 - 也不能 - 解码非ASCII Unicode字符的任何转义,例如`\ u`转义. (4认同)
  • 仅供参考,此功能在技术上并不公开。请参阅https://bugs.python.org/issue30588 (2认同)

Gre*_*ill 8

ast.literal_eval功能接近,但它预计字符串第一个被正确引用.

当然,Python对反斜杠转义的解释取决于字符串的引用方式(""vs r""vs u"",三引号等),因此您可能希望将用户输入包装在合适的引号中并传递给literal_eval.将它包装在引号中也会阻止literal_eval返回数字,元组,字典等.

如果用户键入要打包在字符串周围的类型的不带引号的引号,事情仍然会变得棘手.

  • @ dln385:`literal_eval`从不执行代码.从文档中,"这可以用于安全地评估包含来自不受信任来源的Python表达式的字符串,而无需自己解析值." (5认同)

归档时间:

查看次数:

79754 次

最近记录:

6 年,7 月 前