Преобразование кодовых точек Unicode вручную в UTF-8 и UTF-16 - PullRequest
34 голосов
/ 05 июня 2011

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

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

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

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

Буду очень признателен за любую помощь, поскольку я пытаюсь обдумать это.

Ответы [ 3 ]

48 голосов
/ 05 июня 2011

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

Самое ясное описание правил кодирования кодовых точек UCS в UTF-8, которое я видел, взято из справочной страницы 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, найдя их диапазон, преобразовав из шестнадцатеричного в двоичное,вставка битов в соответствии с приведенными выше правилами, а затем преобразование обратно в шестнадцатеричное значение:

U+4E3E

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

   1110xxxx 10xxxxxx 10xxxxxx

0x4E3E - это 100111000111110b.Бросьте биты в x выше (начните справа, мы заполним пропущенные биты в начале с 0):

   1110x100 10111000 10111110

Осталось место x вначало, заполните его 0:

   11100100 10111000 10111110

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

   0xE4 0xB8 0xBE
37 голосов
/ 05 июня 2011

Описания в Википедии для 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) биты = 80-7FF шестнадцатеричные
3-байтовые UTF-8 = 1110xxxx 10xxxxxx 10xxxxxx bin = 4 + 6 + 6 (16) биты = 800-FFFF hex
4-байтовый UTF-8 = 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx bin = 3 + 6 + 6 + 6 (21) бит = 10000-10FFFF hex

Кодовые точки Unicode не определены выше 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 к 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, закодированный следующим образом:

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

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

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

Теперь у нас есть еще одна проблема.Некоторые машины сначала сохраняют два байта 16-разрядного младшего байта слова (так называемые машины с прямым порядком байтов), а некоторые - сначала старшие байты (машины с прямым порядком байтов).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

С нулевым окончанием, 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 = D83CDC31 hex

4 голосов
/ 05 июня 2011

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

#!/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";
...