与 utf-16 斗争 encoding/decoding

Struggling with utf-16 encoding/decoding

我正在解析包含一些 UTF-16 编码字符串的文档。

我有一个包含以下内容的字节字符串:

my_var = b'\xc3\xbe\xc3\xbf\x004\x004\x000\x003\x006\x006\x000\x006\x00-\x001\x000\x000\x003\x008\x000\x006\x002\x002\x008\x005'

转换为 utf-8 时,我得到以下输出:

print(my_var.decode('utf-8'))
#> þÿ44036606-10038062285

前两个字符 þÿ 表示它是 UTF-16BE 的 BOM,as indicated on Wikipedia

但是,我不明白的是,如果我像这样尝试 UTF16 BOM:

if value.startswith(codecs.BOM_UTF16_BE)

这 returns 错误。事实上,打印 codecs.BOM_UTF16_BE 不会显示相同的结果:

print(codecs.BOM_UTF16_BE)
#> b'\xfe\xff'

这是为什么?我怀疑高端存在一些环境问题,但不确定如何解决该问题。

已经有一些关于如何在 Whosebug 上解码 UTF-16 的提及(如 this one),他们都说一件事:使用 utf-16 和 Python 解码将处理 BOM。

...但这对我不起作用。

print(my_var.decode('utf-16')
#> 뻃뿃㐀㐀 ㌀㘀㘀 㘀ⴀ㄀  ㌀㠀 㘀㈀㈀㠀㔀

但使用 UTF-16BE:

print(my_var.decode('utf-16be')
#> 쎾쎿44036606-10038062285

(不删除bom)

并且使用 UTF-16LE:

print(my_var.decode('utf-16le')
#> 뻃뿃㐀㐀 ㌀㘀㘀 㘀ⴀ㄀  ㌀㠀 㘀㈀㈀㠀㔀

所以,出于我无法解释的原因,仅使用 .decode('UTF-16') 对我不起作用。为什么?

更新

原始源字符串不是我提到的那个,而是这个:

source = '67[=18=]04[=18=]04[=18=]00[=18=]03[=18=]06[=18=]06[=18=]00[=18=]06[=18=]0-[=18=]01[=18=]00[=18=]00[=18=]03[=18=]08[=18=]00[=18=]06[=18=]02[=18=]02[=18=]08[=18=]05'

我使用以下方法转换它:

def decode_8bit(cls, match):
    value = match.group().replace(b'\', b'')
    return chr(int(value, base=8)).encode('utf-8')

my_var = re.sub(b'\\[0-9]{1,3}', decode_8bit, source)

也许我这里做错了什么?

如果使用CP1252编码,þÿ表示UTF-16BE的BOM是对的。

区别如下:

你的第一个字节是0xC3,二进制是11000011

  • UTF-8:

前两位已设置,表示您的 UTF-8 字符长度为 2 个字节。 为您的第一个字符获取 0xC3 0xBE,对于 UTF-8 是 þ。

  • CP1252

CP1252 始终为 1 个字节长,returns à 对于 0xC3。

但是如果您在链接的 BOM 列表中查找 0xC3,您将找不到任何匹配的编码。 看起来一开始就没有 BOM。

使用默认编码可能是可行的方法,即 UTF-16LE for Windows。

添加原始来源后编辑

您对 UTF-8 的编码破坏了 BOM,因为它不是有效的 UTF-8。尽量避免解码并传递字节列表。

OPs解决方案:

bytes(int(value, base=8))

根据@Tomalak 和@Hyarus 的要求,我的问题原因如下:

解码 8 位值时,我将它们作为 UTF-8 编码返回:

def decode_8bit(cls, match):
    value = match.group().replace(b'\', b'')
    return chr(int(value, base=8)).encode('utf-8')

my_var = re.sub(b'\\[0-9]{1,3}', decode_8bit, source)

这弄乱了返回的数据,因为它不是使用 UTF-8 编码的 (duh)。所以正确的代码应该是:

def decode_8bit(cls, match):
    value = match.group().replace(b'\', b'')
    return bytes(int(value, base=8))

my_var = re.sub(b'\\[0-9]{1,3}', decode_8bit, source)

希望对其他人有所帮助...祝您编码顺利! :/