我正在与一个需要字符串的“ 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
所以我的两个问题交织在一起:
答案 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-le
和utf-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-le
或utf-16-be
作为编码的一部分。
这很清楚:
>>> 'abc'.encode('utf-16').hex()
'fffe610062006300'
>>> 'abc'.encode('utf-16-le').hex()
'610062006300'
>>> 'abc'.encode('utf-16-be').hex()
'006100620063'