Подтвердить что ты не робот

Ручное преобразование кодов Unicode в UTF-8 и UTF-16

У меня экзамен по университетскому программированию, и один раздел находится в Юникоде.

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

Вопрос будет примерно таким:

Строка 'mЖ 丽' содержит эти кодовые страницы unicode U+006D, U+0416 и U+4E3D, с ответами, написанными в шестнадцатеричном формате, вручную закодировать строка в UTF-8 и UTF-16.

Любая помощь вообще будет оценена по достоинству, так как я пытаюсь разгадать это.

4b9b3361

Ответ 1

Ого. С одной стороны, я очень рад узнать, что университетские курсы учат реальности, что кодировки символов - это тяжелая работа, но на самом деле знание правил кодирования UTF-8 звучит так, как ожидали многого. (Будет ли это помогать студентам пройти тест Турции?)

Яркое описание, которое я видел до сих пор для правил кодирования кодовых точек UCS для UTF-8, относится к man-странице utf-8(7) во многих системах Linux:

Encoding
   The following byte sequences are used to represent a
   character.  The sequence to be used depends on the UCS code
   number of the character:

   0x00000000 - 0x0000007F:
       0xxxxxxx

   0x00000080 - 0x000007FF:
       110xxxxx 10xxxxxx

   0x00000800 - 0x0000FFFF:
       1110xxxx 10xxxxxx 10xxxxxx

   0x00010000 - 0x001FFFFF:
       11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

   [... removed obsolete five and six byte forms ...]

   The xxx bit positions are filled with the bits of the
   character code number in binary representation.  Only the
   shortest possible multibyte sequence which can represent the
   code number of the character can be used.

   The UCS code values 0xd800–0xdfff (UTF-16 surrogates) as well
   as 0xfffe and 0xffff (UCS noncharacters) should not appear in
   conforming UTF-8 streams.

Возможно, было бы легче запомнить "сжатую" версию диаграммы:

Начальные байты начинающихся искаженных кодовых точек начинаются с 1 и добавляются прокладки 1+0. Последующие байты начинаются 10.

0x80      5 bits, one byte
0x800     4 bits, two bytes
0x10000   3 bits, three bytes

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

2**(5+1*6) == 2048       == 0x800
2**(4+2*6) == 65536      == 0x10000
2**(3+3*6) == 2097152    == 0x200000

Я знаю, что я мог запомнить правила, чтобы получить диаграмму проще, чем сама диаграмма. Здесь вы надеетесь, что хорошо помните правила.:)

Обновление

После того, как вы построили диаграмму выше, вы можете преобразовать входные кодовые обозначения Unicode в UTF-8, найдя их диапазон, преобразовывая из шестнадцатеричного в двоичный, вставляя биты в соответствии с вышеприведенными правилами, а затем преобразовывая назад в hex:

U+4E3E

Это соответствует диапазону 0x00000800 - 0x0000FFFF (0x4E3E < 0xFFFF), поэтому представление будет иметь вид:

   1110xxxx 10xxxxxx 10xxxxxx

0x4E3E 100111000111110b. Отбросьте биты в x выше (начните с правой стороны, мы будем заполнять отсутствующие биты в начале с помощью 0):

   1110x100 10111000 10111110

В начале есть пятно x, которое заполняется с помощью 0:

   11100100 10111000 10111110

Преобразовать из бит в шестнадцатеричный:

   0xE4 0xB8 0xBE

Ответ 2

Описание в Wikipedia для UTF-8 и UTF-16 хороши:

Процедуры для строки вашего примера:

UTF-8

UTF-8 использует до 4 байтов для представления кодовых точек Unicode. Для 1-байтового случая используйте следующий шаблон:

1-байтовый UTF-8 = 0xxxxxxx bin= 7 бит = 0-7F hex

Начальный байт 2-, 3- и 4-байтового UTF-8 начинается с 2, 3 или 4 одного бита, за которым следует нулевой бит. Последующие байты всегда начинаются с двухбитового шаблона 10, оставляя 6 бит для данных:

2-байтовый UTF-8 = 110xxxxx 10xxxxxx bin= 5 + 6 (11) bits = 80-7FF hex
3-байтовый UTF-8 = 1110xxxx 10xxxxxx 10xxxxxx bin= 4 + 6 + 6 (16) bits = 800-FFFF hex
4-байтовый UTF-8 = 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx bin= 3 + 6 + 6 + 6 (21) bits = 10000-10FFFF hex

Кодовые страницы Unicode равны undefined за пределами 10FFFF hex.

Вашими кодовыми точками являются U + 006D, U + 0416 и U + 4E3D, требующие 1-, 2- и 3-байтовые последовательности UTF-8, соответственно. Преобразуйте в двоичный код и назначьте бит:

U + 006D = 1101101 bin= 0 1101101 bin= 6D hex
U + 0416 = 10000 010110 bin= 110 10000 10 010110 bin= D0 96 hexсуб >
U + 4E3D = 0100 111000 111101 bin= 1110 0100 10 111000 10 111101 bin= E4 B8 BD hex

Конечная последовательность байтов:

6D D0 96 E4 B8 BD

или если требуются строки с нулевым завершением:

6D D0 96 E4 B8 BD 00

UTF-16

UTF-16 использует 2 или 4 байта для представления кодов Unicode. Алгоритм:

U + 0000 to U + D7FF использует 2-байтовый 0000 hex для D7FF hex
U + D800 до U + DFFF являются недопустимыми кодовыми точками, зарезервированными для 4-байтовых UTF-16
U + E000 - U + FFFF использует 2 байта E000 hex для FFFF hex

U + 10000 до U + 10FFFF использует 4-байтовое кодирование UTF-16 следующим образом:

  • Выделите 10000 hex из кодового пункта.
  • Экспресс результат как 20-битный двоичный файл.
  • Используйте шаблон 110110xxxxxxxxxx 110111xxxxxxxxxx bin для кодирования верхних и нижних 10 бит в два 16-разрядных слова.

Использование ваших кодовых точек:

U + 006D = 006D hex
U + 0416 = 0416 hex
U + 4E3D = 4E3D hex

Теперь у нас есть еще одна проблема. На некоторых машинах хранятся два байта первого младшего байтового разряда младшего разряда (так называемые малоконечные машины) и некоторые из них хранят наиболее значимые байты (машины большого конца). UTF-16 использует кодовую точку U + FEFF (называемую знаком байта или спецификацией), чтобы помочь машине определить, содержит ли байтовый поток большой или малозначный UTF-16:

big-endian = FE FF 00 6D 04 16 4E 3D
little-endian = FF FE 6D 00 16 04 3D 4E

С nul-term, U + 0000 = 0000 hex:

big-endian = FE FF 00 6D 04 16 4E 3D 00 00
little-endian = FF FE 6D 00 16 04 3D 4E 00 00

Поскольку ваш инструктор не дал код, который требовал 4-байтового UTF-16, вот один пример:

U + 1F031 = 1F031 hex - 10000 hex= F031 hex= 0000111100 0000110001 bin=
110110 0000111100 110111 0000110001 bin= D83C DC31 hex

Ответ 3

Следующая программа выполнит необходимую работу. Это может быть недостаточно "ручным" для ваших целей, но, как минимум, вы можете проверить свою работу.

#!/usr/bin/perl

use 5.012;
use strict;
use utf8;
use autodie;
use warnings;
use warnings    qw< FATAL utf8 >;
no warnings     qw< uninitialized >;
use open        qw< :std :utf8 >;
use charnames   qw< :full >;
use feature     qw< unicode_strings >;

use Encode              qw< encode decode >;
use Unicode::Normalize  qw< NFD NFC >;

my ($x) = "mЖ丽";

open(U8,">:encoding(utf8)","/tmp/utf8-out");
print U8 $x;
close(U8);
open(U16,">:encoding(utf16)","/tmp/utf16-out");
print U16 $x;
close(U16);
system("od -t x1 /tmp/utf8-out");
my $u8 = encode("utf-8",$x);
print "utf-8: 0x".unpack("H*",$u8)."\n";

system("od -t x1 /tmp/utf16-out");
my $u16 = encode("utf-16",$x);
print "utf-16: 0x".unpack("H*",$u16)."\n";