unicode endian困惑了我

时间:2012-09-08 06:59:52

标签: python encoding utf-8 endianness ucs2

我用三种不同的形式编辑三个文件,其中包含相同内容“你”(英语为you) - gbk \ utf-8 \ ucs-2,gedit名为“ok1,ok2,ok3 ”

>>> f1 = open('ok1', 'rb').read()
>>> f2 = open('ok2', 'rb').read()
>>> f3 = open('ok3', 'rb').read()
>>> f1
'\xc4\xe3\n'
>>> f2
'\xe4\xbd\xa0\n'
>>> f3
'`O\n\x00'
>>> hex(ord("`"))
'0x60'
>>> hex(ord("O")) 
'0x4f'

实际上f3是'\ x60 \ x4f', 但以下输出让我困惑

>>> '\xe4\xbd\xa0'.decode("utf-8")
u'\u4f60'
>>> '\xc4\xe3'.decode("gbk")
u'\u4f60'
>>> 

为什么只有ucs-2中的字节序问题(或说unicode),而不是utf-8,而不是gbk?

2 个答案:

答案 0 :(得分:5)

UTF-8 GBK 以字节序列存储数据。强烈定义了在这些编码之后的字节值。该字节顺序不会随编码,传输或解码中使用的架构而改变。

另一方面, UCS-2 或新的 UTF-16 以2字节的顺序存储数据。这些2字节标记中的单个字节的顺序是 endianness ,它取决于底层的机器体系结构。在与 UCS-2 中编码的数据进行通信之前,系统必须就如何识别令牌的字节顺序达成一致。

在您的情况下,Unicode点U + 4F60在 UCS-2 中编码为单个2字节令牌0x4F60。由于您的机器将最低有效字节放在内存对齐中最重要的字节之前,因此序列('0x60', '0x4F')已放入文件中。因此,文件读取将按此顺序生成字节。

Python仍然可以正确解码这些数据,因为它会在形成2字节令牌之前以正确的顺序读取字节:

>>> '`O\n\x00'.decode('utf-16')
u'\u4f60\n'

答案 1 :(得分:4)

Endian-ness仅适用于多字节字,但UTF-8使用8位单位来编码信息(这就是名称中的8代表的信息)。从来没有混淆订购的问题。

有时可能需要不止一个单元来编码信息,但它们被认为是不同的。例如,字母A是一个字节0x41。当必须对具有更多字节的字符进行编码时,它使用前导指示符字节,然后使用额外的连续字节来捕获该字符所需的所有信息。从逻辑上讲,这些是不同的单位。

GBK使用类似的方案;字符使用1字节的单位,就像UTF-8一样,第二个字节可以用于某些字符。

另一方面,UCS-2(以及它的继任者,UTF-16)是一种2字节格式。它以16位为单位对信息进行编码,这16位总是在一起。该单元中的2个字节在逻辑上属于一个整体,现代架构将它们视为一个单元,因此决定了它们的存储顺序。这就是endianess的用武之地,单元中2个字节的顺序取决于体系结构。在您的架构中,使用little-endianess对字节进行排序,这意味着“较小”字节首先出现。这就是0x4F字节位于文件中0x60字节之前的原因。

请注意,python可以读取大或小端UTF-16就好了;如果开头没有指标字符(字节顺序标记或BOM),您可以明确选择结束:

>>> '`O\n\x00'.decode('utf-16')
u'\u4f60\n'
>>> '`O\n\x00'.decode('utf-16-le')
u'\u4f60\n'
>>> 'O`\x00\n'.decode('utf-16-be')
u'\u4f60\n'

在后一个例子中,字节已被反转,并被解码为big-endian。