Требуется ли префикс [0xff, 0xfe] для строк в кодировке utf-16? - PullRequest
0 голосов
/ 14 ноября 2018

Переписанный вопрос!

Я работаю с устройством поставщика, для которого требуется "кодировка Unicode" строк, где каждый символ представлен двумя байтами.Мои строки всегда будут основаны на ASCII, поэтому я подумал, что это будет способ перевести мою строку в строку поставщика:

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

Но, изучая результат, я вижу, что есть ведущий [0xff, 0xfe]на bytearray:

>>> [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 на заголовке закодированных байтов?
  • Есть ли опасность снятия префикса 0xff, 0xfe, как с b2 выше?

Ответы [ 5 ]

0 голосов
/ 15 ноября 2018

Это наблюдение

... что меня действительно удивляет, что я могу декодировать b1 и b2, и они оба восстанавливаются до исходной строки:

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

предлагаетесть встроенное значение по умолчанию, потому что для 16-битных кодов UTF-16 есть два возможных варианта: Big and Little Endian .

Обычно Python выводит порядковый номер для использованияиз спецификации при чтении - и поэтому всегда добавляет один при записи.Если вы хотите задать конкретный порядок байтов, вы можете использовать явные кодировки utf-16-le и utf-16-be:

… при использовании такой кодировки спецификация будет автоматически записана как первый символи будет молча отброшен при чтении файла.Существуют варианты этих кодировок, такие как «utf-16-le» и «utf-16-be» для кодировок с прямым порядком байтов и с прямым порядком байтов, которые задают один конкретный порядок байтов и не пропускают спецификацию.
(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/, my emph.)

Все же это работает для вас. Если мы посмотрим в исходном коде 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, а затем следует спецификация, порядок байтов устанавливается равным всему, что это указывает.порядок "в этом последнем комментарии относится к тому, был ли определенный порядок байтов явно объявлен ИЛИ имеет bвстречаются с помощью спецификации;и когда ни один из них не соответствует истине, он будет использовать порядковый номер вашей системы.

0 голосов
/ 15 ноября 2018

Полезны ответы и особенно комментарий от 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'
0 голосов
/ 14 ноября 2018

Это метка порядка байтов, a.k.a. BOM: см. https://en.wikipedia.org/wiki/UTF-16 (см. Схемы кодирования порядка суббайт gByte). Его цель - позволить декодеру определить, является ли кодировка прямым или старшим.

0 голосов
/ 14 ноября 2018

Это метка порядка байтов Unicode, закодированная в UTF-16. Его цель - сообщить о порядке байтов читателю, ожидающему текст, закодированный в кодировке Unicode.

Вы можете опустить его, если читатель иначе знает или узнает порядок байтов.

'abc'.encode('utf-16-le')
0 голосов
/ 14 ноября 2018

Это знак порядка байтов . Это префикс к документу UTF, который указывает, что endianness документ использует. Это достигается путем кодирования кодовой точки 0xFEFF в порядке байтов - в данном случае, с прямым порядком байтов (сначала младший байт). Любой, кто пытается прочитать его в обратном порядке, с прямым порядком байтов (сначала более значимый байт), будет читать первый символ как 0xFFFE, то есть код, который определенно не является допустимым символом, информируя читателя о необходимости ошибки или переключите порядок байтов для остальной части документа.

...