与Python 2中的“b”…'.decode(“utf8”,“反斜杠替换”)`完全等价

2024-09-29 21:41:34 发布

您现在位置:Python中文网/ 问答频道 /正文

在python3.5中,.decode("utf-8", "backslashreplace")是处理部分Unicode、部分未知的遗留编码二进制字符串的一个非常好的选项。有效的UTF-8序列将被解码,无效的将作为转义序列保留。例如

>>> print(b'\xc2\xa1\xa1'.decode("utf-8", "backslashreplace"))
¡\xa1

这就失去了b'\xc2\xa1\xa1'b'\xc2\xa1\\xa1'之间的区别,但是如果你是在“给我买点东西吧,不要太浪费,我以后可以用手来修好”的心态,那可能没问题。在

但是,这是Python3.5中的一个新特性。我正在开发的程序也需要支持3.4和2.7。在这些版本中,它抛出一个异常:

^{pr2}$

我找到了一个近似值,但不是完全等价的:

>>> print(b'\xc2\xa1\xa1'.decode("latin1")
...       .encode("ascii", "backslashreplace").decode("ascii"))
\xc2\xa1\xa1

非常重要的是,行为不依赖于解释器版本。有人能建议一种方法来精确地获得python3.5在2.7和3.4中的行为吗?在

(旧版本的2.x或3.x都不需要工作。猴子修补codecs完全可以接受。)


Tags: 字符串版本编码选项asciiunicode二进制序列
2条回答

您可以编写自己的错误处理程序。下面是我在Python2.7、3.3和3.6上测试的解决方案:

from __future__ import print_function
import codecs
import sys

print(sys.version)

def myreplace(ex):
    # The error handler receives the UnicodeDecodeError, which contains arguments of the
    # string and start/end indexes of the bad portion.
    bstr,start,end = ex.object,ex.start,ex.end

    # The return value is a tuple of Unicode string and the index to continue conversion.
    # Note: iterating byte strings returns int on 3.x but str on 2.x
    return u''.join('\\x{:02x}'.format(c if isinstance(c,int) else ord(c))
                    for c in bstr[start:end]),end

codecs.register_error('myreplace',myreplace)
print(b'\xc2\xa1\xa1ABC'.decode("utf-8", "myreplace"))

输出:

^{bq}$

我尝试了一个更完整的后端口cpython implementation

这将同时处理UnicodeDecodeError(来自.decode())以及来自.encode()和来自.translate()的{}:

from __future__ import unicode_literals

import codecs


def _bytes_repr(c):
    """py2: bytes, py3: int"""
    if not isinstance(c, int):
        c = ord(c)
    return '\\x{:x}'.format(c)


def _text_repr(c):
    d = ord(c)
    if d >= 0x10000:
        return '\\U{:08x}'.format(d)
    else:
        return '\\u{:04x}'.format(d)


def backslashescape_backport(ex):
    s, start, end = ex.object, ex.start, ex.end
    c_repr = _bytes_repr if isinstance(ex, UnicodeDecodeError) else _text_repr
    return ''.join(c_repr(c) for c in s[start:end]), end


codecs.register_error('backslashescape_backport', backslashescape_backport)

print(b'\xc2\xa1\xa1after'.decode('utf-8', 'backslashescape_backport'))
print(u'\u2603'.encode('latin1', 'backslashescape_backport'))

相关问题 更多 >

    热门问题