【问题标题】:Struggling with utf-16 encoding/decoding挣扎于 utf-16 编码/解码
【发布时间】:2019-02-25 12:53:38
【问题描述】:

我正在解析一个包含一些 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)

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

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

为什么会这样?我怀疑高端存在一些问题,但不知道如何解决。

已经有一些关于如何在 Stackoverflow 上解码 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 = '\376\377\0004\0004\0000\0003\0006\0006\0000\0006\000-\0001\0000\0000\0003\0008\0000\0006\0002\0002\0008\0005'

我使用以下方法对其进行了转换:

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-16 BOM 为 0xFE 0xFF。您的输入还有其他内容。可能相关stackoverflow.com/questions/11546351/…
  • 您提供的二进制序列不是有效的 UTF-16。检查print(...) 的结果不是检查编码的有效方法,因为print 可能不会打印某些字符,所以你不应该相信它。
  • @Tomalak,我已经更新了我的问题(最后)。我忘了提到原始来源,也许它改变了一切?

标签: python utf-16


【解决方案1】:

根据@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)

希望对其他人有所帮助...祝编码好运! :/

【讨论】:

  • 你知道确切的原因吗​​? chr() 不返回 255 吗?还是 encode() 不惜一切代价强制执行 UTF-8 并切换一些位?
  • 很遗憾,没有,我不知道。我在多个不同的来源上运行我的代码,一切正常,所以很好。但我不知道是什么导致了这个问题。
【解决方案2】:

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

区别如下:

您的第一个字节是 0xC3,即二进制 11000011。

  • UTF-8

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

  • CP1252

CP1252 始终为 1 字节长,并为 0xC3 返回 Ã

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

使用默认编码可能是要走的路,Windows 是UTF-16LE

添加原始来源后编辑

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

OP 解决方案:

bytes(int(value, base=8))

【讨论】:

  • 谢谢。我在我的问题中添加了更多细节,解释了我是如何获得 my_var 值的,也许我首先在那里做错了什么?
  • @CyrilN。 \376\377 是您在 base8 中的 BOM。您对 UTF-8 的编码可能会破坏它,因为它不是有效的 UTF-8。如果没有其他方法,请尽量避免解码并传递字节列表或使用单字节编码。 python 专家可能会对此更有帮助。
  • 天哪,用bytes(int(value, base=8)) 替换chr(int(value, base=8)).encode('utf-8') 成功了!
  • @CyrilN。你能写一个答案,以及发生的一些推理吗?我认为这可能是有益的,因为这里至少还有一个人有“c3 82 c2 bf”字节序列,而那里的线程有些不确定。
  • @CyrilN。我将您的解决方案添加到我的答案中。如果您想提供自己的答案,请告诉我。在这种情况下,我会尽快删除它。如果你知道为什么会这样,我会鼓励你这样做。我试图研究 chr() 和 encode() 函数,但无法解释为什么 0xFF 0xFE 被转换为 0xC3 0x82 0xC2 0xBF。
猜你喜欢
  • 1970-01-01
  • 2012-03-13
  • 2020-08-02
  • 2012-06-20
  • 2010-12-27
  • 1970-01-01
  • 2011-08-18
  • 2023-04-06
  • 2014-08-30
相关资源
最近更新 更多