【问题标题】:Python3 : unescaping non ascii charactersPython3:取消转义非 ascii 字符
【发布时间】:2013-08-31 13:49:11
【问题描述】:

(Python 3.3.2) 我必须取消对 re.escape() 调用返回的一些非 ASCII 转义字符的转义。我看到 herehere 方法不起作用。我在 100% UTF-8 环境中工作。

# pure ASCII string : ok
mystring = "a\n" # expected unescaped string : "a\n"
cod = codecs.getencoder('unicode_escape')
print( cod(mystring) )

# non ASCII string : method #1
mystring = "€\n"
# equivalent to : mystring = codecs.unicode_escape_decode(mystring)
cod = codecs.getdecoder('unicode_escape')
print(cod(mystring))
# RESULT = ('â\x82¬\n', 5) INSTEAD OF ("€\n", 2)

# non ASCII string : method #2
mystring = "€\n"
mystring = bytes(mystring, 'utf-8').decode('unicode_escape')
print(mystring)
# RESULT = â\202¬ INSTEAD OF "€\n"

这是一个错误吗?我是不是误会了什么?

任何帮助将不胜感激!

PS : 感谢 Michael Foukarakis 的评论,我编辑了我的帖子。

【问题讨论】:

  • 你是在哪里执行终端/cmd中的文件还是?
  • "€\\n" 不是 Unicode 转义字符串,因此您无法将其解码为任何有意义的内容。 "€\n",如果是 Unicode 转义,将变为 b'\\u20ac\\n'。所以,是的,你似乎误解了编码。
  • 一个好点:我编辑了我的帖子。但我的问题与(非 unicode)€ 字符相同。
  • badcOre > 输出存储在文件中并在终端 (urxvt) 中打印。

标签: python unicode python-3.x


【解决方案1】:

我猜你需要处理的实际字符串是mystring = €\\n

mystring = "€\n"  # that's 2 char, "€" and new line
mystring = "€\\n" # that's 3 char, "€", "\" and "n"

我不太明白python3的encode()decode()里面出了什么问题,但是我的朋友在我们写一些工具的时候解决了这个问题。

我们的做法是在转义过程完成后绕过encoder("utf_8")

>>> "€\\n".encode("utf_8")
b'\xe2\x82\xac\\n'
>>> "€\\n".encode("utf_8").decode("unicode_escape")
'â\x82¬\n'
>>> "€\\n".encode("utf_8").decode("unicode_escape").encode("utf_8")
b'\xc3\xa2\xc2\x82\xc2\xac\n'  # we don't want this
>>> bytes([ord(char) for char in "€\\n".encode("utf_8").decode("unicode_escape")])
b'\xe2\x82\xac\n'  # what we really need
>>> str(bytes([ord(char) for char in "€\\n".encode("utf_8").decode("unicode_escape")]), "utf_8")
'€\n'

我们可以看到:虽然decode("unicode_escape") 的结果看起来是连贯的,但bytes 对象实际上包含字符串的正确字节(使用utf-8 编码),在本例中为"\xe2\x82\xac\n"

我们现在不直接打印str对象,我们也不使用encode("utf_8"),我们使用ord()来创建bytes对象b'\xe2\x82\xac\n'

你可以从这个bytes对象中得到正确的str,只要把它放到str()中就行了


顺便说一句,我和朋友想要做的工具是一个包装器,它允许用户输入类似 c 的字符串文字,并自动转换转义序列。

User input:\n\x61\x62\n\x20\x21  # 20 characters, which present 6 chars semantically
output:  # \n
ab       # \x61\x62\n
 !       # \x20\x21

这是用户在终端输入一些不可打印字符的强大工具。

我们的最终工具是:

#!/usr/bin/env python3
import sys 

for line in sys.stdin:
    sys.stdout.buffer.write(bytes([ord(char) for char in line[:-1].encode().decode('unicode_escape')]))
    sys.stdout.flush()

【讨论】:

    【解决方案2】:

    您似乎误解了编码。为了防止常见错误,我们通常在字符串离开应用程序时对其进行编码,并在它进入时对其进行解码。

    首先,让我们看一下 unicode_escape 的文档,其中指出:

    产生[s] 一个适合作为 Python 源代码中的 Unicode 文字的字符串。

    以下是您从网络或声称其内容是 Unicode 转义的文件中获得的内容:

    b'\\u20ac\\n'
    

    现在,您必须对其进行解码才能在您的应用中使用它:

    >>> s = b'\\u20ac\\n'.decode('unicode_escape')
    >>> s
    '€\n'
    

    如果你想把它写回,比如说,一个 Python 源文件:

    with open('/tmp/foo', 'wb') as fh: # binary mode
        fh.write(b'print("' + s.encode('unicode_escape') + b'")')
    

    【讨论】:

    • 感谢您的回答。我的“编码”字符串(例如“\€\n”)有一个非常 Pythonic 的起源:它是调用 re.escape() 返回的值。据我所知,没有像 re.unescape() 这样的反函数。因此,我尝试解码“转义”字符串。我怎样才能做到这一点?
    • “哪种编码合适?”问题的答案取决于它将如何使用。那么,您的用例是什么?另外,您确定re.escape 是必要的,即您是否使用用户输入作为正则表达式?
    • 这些字符串是从 UTF-8 编码文件中读取的,并将作为 UTF-8 字符串写入另一个文件中。幸运的是,我没有混合不同的编码。
    【解决方案3】:
    import string
    printable = string.printable
    printable = printable + '€'
    
    def cod(c):
        return c.encode('unicode_escape').decode('ascii')
    
    def unescape(s):
        return ''.join(c if ord(c)>=32 and c in printable else cod(c) for c in s)
    
    mystring = "€\n"
    print(unescape(mystring))
    

    很遗憾,string.printable 仅包含 ASCII 字符。您可以像我在此处所做的那样制作一个副本,并使用您喜欢的任何 Unicode 字符对其进行扩展,例如

    【讨论】:

      猜你喜欢
      • 2014-06-12
      • 2021-05-21
      • 1970-01-01
      • 2011-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-30
      相关资源
      最近更新 更多