【问题标题】:How can I decode a utf-8 byte array to a string in Python2?如何在 Python2 中将 utf-8 字节数组解码为字符串?
【发布时间】:2016-12-08 05:14:57
【问题描述】:

我有一个表示 utf-8 编码字符串的字节数组。我想将这些字节解码回 Pyton2 中的字符串。我的整个程序都依赖 Python2,所以我无法切换到 Python3。

array = [67, 97, 102, **-61, -87**, 32, 70, 108, 111, 114, 97] 

-> 咖啡馆é 植物区系

由于我想要的字符串中的每个字符不一定由数组中的 1 个字节表示,因此我不能使用如下解决方案:

"".join(map(chr, array))

我试图创建一个函数来遍历数组,当它遇到一个不在 0-127 (ASCII) 范围内的数字时,创建一个新的 16 位整数,将当前位向左移动 8 位,然后使用按位或添加以下字节。最后它会使用 unichr() 对其进行解码。

result = []


for i in range(len(byte_array)):
    x = byte_array[i]
    if x < 0:
        b16 = x & 0xFFFF # 16 bit
        b16 = b16 << 8
        b16 = b16 | byte_array[i+1]
        result.append(unichr(m16))
    else:
        result.append(chr(x))

return "".join(result)

然而,这并不成功。

下面的文章很好地解释了这个问题,并且包含了一个 nodeJS 解决方案:

http://ixti.net/development/node.js/2011/10/26/get-utf-8-string-from-array-of-bytes-in-node-js.html

【问题讨论】:

  • UTF-8 解码不是这样工作的。
  • 你不能用00“填充”0-128范围内的每个数字吗?
  • 正如您在我的回答中看到的那样,您与 map 和 chr 版本的加入几乎可以工作 - 但对于负数的问题。我的回答基本相同,使用更易读的生成器表达式,并处理负数。

标签: python arrays python-2.7 utf-8


【解决方案1】:

您必须记住,Python2 中的“字符串”不是正确的文本,只是内存中的一个字节序列,当您“打印”它们时恰好映射到字符 - 如果预期字符的映射在字节序列与终端中的字节序列匹配,您将看到格式正确的文本。

如果您的终端不是 UTF-8,即使您在内存中获得了正确的字节字符串,仅打印它也会显示错误的结果。这就是为什么在表达式末尾需要额外的“解码”步骤的原因。

text = b''.join(chr(i if i > 0 else 256 + i) for i in array).decode('utf-8')

由于您的源将 128 到 255 之间的数字编码为负数,因此我们使用内联“if”运算符在调用“chr”之前重新规范化该值。

为了清楚起见 - 你说“因为我想要的字符串中的每个字符不一定由数组中的 1 个字节表示” - 所以 - 如果你使用 Python2.x 字符串,需要注意的是终端无论如何。如果您想处理正确的 tet,在将您的数字连接到正确的(字节)字符串后,使用“解码”方法 - 这是了解 UTF-8 多字节编码字符并返回给您的部分一个(文本)字符串对象(Python 2 中的一个 'unicode' 对象)——它将每个字符视为一个实体。

【讨论】:

  • i if i &gt; 0 else 256 + i也可以写成i &amp; 0xFF
  • 它可能会起作用,尽管出于语义原因,我不建议这样做。它滥用“&”操作的副作用只是为了使数字为正。也许“最正确”的方式是@Joran's answer using struct - 但我不确定这样做的效果。这种方式是“在字符串中思考”的方式。 :-)
【解决方案2】:

使用很少使用的 array module 将您的输入转换为字节串,然后使用 UTF-8 编解码器将其转换为 decode

import array
decoded = array.array('b', your_input).tostring().decode('utf-8')

【讨论】:

  • ValueError: byte must be in range(0, 256)
  • 这给了我一个 ValueError: byte must be in range(0, 256)
  • @wim:哦,嗯。它们在输入中有符号值而不是无符号值。那么这不会直接起作用。
  • @wim: 解码方法切换到带符号值的东西。
  • 在 Python 3 中,tostring() 被重命名为 tobytes(),否则,这很好用。
【解决方案3】:

您可以为此使用struct.pack

>>> a =  [67, 97, 102, -61, -87, 32, 70, 108, 111, 114, 97]
>>> struct.pack("b"*len(a),*a)
'Caf\xc3\xa9 Flora'
>>> print struct.pack("b"*len(a),*a).decode('utf8')
Café Flora

【讨论】:

    猜你喜欢
    • 2020-03-09
    • 2013-05-11
    • 2019-09-15
    • 2021-08-28
    • 2019-12-13
    • 1970-01-01
    • 2012-06-05
    • 2018-05-13
    • 1970-01-01
    相关资源
    最近更新 更多