2012-09-08 62 views
5

我编辑了三个文件,它们有三个不同的形式 - “gbk \ utf-8 \ ucs-2”,其中gedit命名为“ok1,ok2,ok3”,它有相同的内容“你”(英文为you) ”。unicode endian困惑我

>>> 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中?

回答

5

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

在另一方面,UCS-2或新UTF-16商店中的2个字节的序列的数据。这些2字节标记内的单个字节的顺序是排列顺序,它取决于底层的机器体系结构。系统必须就如何识别令牌的字节顺序达成一致,然后才能与编码为UCS-2的数据进行通信。

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

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

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

由于您的机器在内存对齐中的最重要字节之前放置了最低有效字节,所以序列('0x60','0x4F')已被放入文件中。因此,文件读取将按此顺序产生字节。为什么在我的机器中,f1不是'\ xe3 \ xc4 \ n'? f2不是f2'\ xbd \ xe4 \ xa0 \ n' –

+0

@Dd Pp:因为编写utf-8文件时,gedit会将字节*逐个*。但是,在编写ucs-2编码文件时,gedit会将字节*两乘二*。字节内的顺序仅在后一种情况下依赖于字节顺序。 –

3

字节序只适用于多字节字,但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' 

在后一个例子字节已经发生了逆转,并作为解码大端。