utf-16编码的字符串是否需要[0xff,0xfe]前缀?

时间:2018-11-14 16:58:21

标签: python-3.x utf-16

重写的问题!

我正在与一个需要字符串的“ unicode编码”的供应商设备一起工作,其中每个字符用两个字节表示。我的字符串将始终基于ASCII,因此我认为这是将字符串转换为供应商字符串的方法:

>>> b1 = 'abc'.encode('utf-16')

但是检查结果,我发现字节数组上有一个前导的[0xff,0xfe]:

>>> [hex(b) for b in b1]
['0xff', '0xfe', '0x61', '0x0', '0x62', '0x0', '0x63', '0x0']

由于供应商的设备不希望使用[0xff,0xfe],因此我可以将其剥离...

>>> b2 = 'abc'.encode('utf-16')[2:]
>>> [hex(b) for b in b2]
['0x61', '0x0', '0x62', '0x0', '0x63', '0x0']

...这就是我想要的。

但是令我惊讶的是我可以解码b1和b2,并且它们都重新构造为原始字符串:

>>> b1.decode('utf-16') == b2.decode('utf-16')
True

所以我的两个问题交织在一起:

  • 编码字节头上的0xff,0xfe有什么意义?
  • 像上面的b2一样,剥离0xff,0xfe前缀是否有任何危险?

5 个答案:

答案 0 :(得分:1)

这是byte order mark。它是UTF文档的前缀,用于指示文档使用的endianness。它通过按字节顺序对代码点0xFEFF进行编码来完成此操作-在这种情况下,使用低位字节序(低有效字节在前)。尝试以其他方式(大字节优先)(高字节在前)进行读取的所有内容,都会将第一个字符读取为0xFFFE,这是一个代码点,该代码点明确不是有效字符,从而告知读者它需要错误或切换文档其余部分的字节序。

答案 1 :(得分:1)

此观察

  

...令我惊讶的是我可以解码b1和b2,它们都重新构成了原始字符串:

b1.decode('utf-16') == b2.decode('utf-16')
True

建议使用内置默认值,因为16位宽的UTF-16代码有两种可能的排列方式:Big and Little Endian

通常,Python会在读取时从BOM推导要使用的字节序,因此在写入时总是加一。如果要强制使用特定的字节序,则可以使用显式编码utf-16-leutf-16-be

  

...使用这种编码时,BOM将自动写为第一个字符,并且在读取文件时将被静默删除。这些编码有多种变体,例如用于Little-endian和Big-endian编码的'utf-16-le'和'utf-16-be',它们指定一个特定的字节顺序并且不跳过BOM。 />   (https://docs.python.org/3/howto/unicode.html#reading-and-writing-unicode-data

但是,如果您不使用特定的顺序,那么将使用什么默认值?原始的Unicode提案PEP 100警告

  

注意:“ utf-16”应通过使用并要求用于文件输入/输出的字节顺序标记(BOM)来实现。
  (https://www.python.org/dev/peps/pep-0100/,我的电话。)

但它对您有效。如果我们在Python源代码中查找如何管理此代码,则会在_codecsmodule.c中找到以下注释:

/* This version provides access to the byteorder parameter of the
   builtin UTF-16 codecs as optional third argument. It defaults to 0
   which means: use the native byte order and prepend the data with a
   BOM mark.
*/

更深层次,在unicodeobject.c

/* Check for BOM marks (U+FEFF) in the input and adjust current
   byte order setting accordingly. In native mode, the leading BOM
   mark is skipped, in all other modes, it is copied to the output
   stream as-is (giving a ZWNBSP character). */

因此,最初,字节顺序被设置为系统的默认值,并且当您开始解码UTF-16数据并跟随BOM时,字节顺序被设置为此指定的值。最后一条注释中的“本机顺序”是指是否已明确声明某个字节顺序,或者是否已通过BOM遇到;并且当都不正确时,它将使用系统的字节序。

答案 2 :(得分:0)

这是字节顺序标记,又称为BOM:请参见https://en.wikipedia.org/wiki/UTF-16(请查看副标题gByte顺序编码方案)。 目的是允许解码器检测编码是小端还是大端。

答案 3 :(得分:0)

这是UTF-16编码的Unicode字节顺序标记。其目的是将字节顺序传达给希望使用Unicode字符编码进行编码的文本的阅读器。

如果读者不知道或不知道字节顺序,则可以忽略它。

'abc'.encode('utf-16-le')

答案 4 :(得分:0)

答案,特别是来自usr2564301的注释很有帮助:0xff 0xfe前缀是“字节顺序标记”,它带有字节序信息和字节串。如果知道所需的字节序,则可以指定utf-16-leutf-16-be作为编码的一部分。

这很清楚:

>>> 'abc'.encode('utf-16').hex()
'fffe610062006300'
>>> 'abc'.encode('utf-16-le').hex()
'610062006300'
>>> 'abc'.encode('utf-16-be').hex()
'006100620063'