it-swarm.dev

Converter manualmente pontos de código unicode em UTF-8 e UTF-16

Eu tenho um exame de programação da universidade chegando e uma seção é em Unicode.

Eu procurei em todo o lado por respostas a isso, e meu palestrante é inútil, de modo que isso não ajuda em nada, portanto esse é o último recurso para vocês possivelmente ajudarem.

A questão será algo como:

A string 'mЖ 丽' possui esses pontos de código unicode U+006D, U+0416 e U+4E3D, com respostas escritas em hexadecimal, codifique manualmente a sequência em UTF-8 e UTF-16.

Qualquer ajuda será muito apreciada, pois estou tentando entender isso.

36
RSM

Uau. Por um lado, estou emocionado em saber que os cursos universitários estão ensinando a realidade de que codificações de caracteres são um trabalho árduo, mas conhecer as regras de codificação UTF-8 parece esperar muito. (Ajudará os alunos passar no teste da Turquia ?)

A descrição mais clara que eu vi até agora para as regras de codificação de pontos de código UCS para UTF-8 são da página de manual utf-8(7) em muitos sistemas 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.

Pode ser mais fácil lembrar uma versão 'compactada' do gráfico:

Os bytes iniciais iniciados dos pontos de código mutilados começam com um 1 e adicione preenchimento 1+0. Bytes subsequentes iniciam 10.

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

Você pode derivar os intervalos anotando quanto espaço você pode preencher com os bits permitidos na nova representação:

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

Eu sei que [~ # ~] i [~ # ~] conseguia lembrar as regras para derivar o gráfico mais facilmente do que o próprio gráfico. Esperamos que você também seja bom em lembrar regras. :)

Atualização

Depois de criar o gráfico acima, você pode converter pontos de código Unicode de entrada em UTF-8, localizando seu intervalo, convertendo de hexadecimal em binário, inserindo os bits de acordo com as regras acima e convertendo novamente em hexadecimal:

U+4E3E

Isso se encaixa no 0x00000800 - 0x0000FFFF alcance (0x4E3E < 0xFFFF), então a representação terá a forma:

   1110xxxx 10xxxxxx 10xxxxxx

0x4E3E é 100111000111110b. Coloque os bits no x acima (comece da direita, preencheremos os bits ausentes no início com 0):

   1110x100 10111000 10111110

Há um ponto x restante no início, preencha-o com 0:

   11100100 10111000 10111110

Converta de bits para hex :

   0xE4 0xB8 0xBE
49
sarnold

As descrições na Wikipedia para TF-8 e TF-16 são boas:

Procedimentos para sua sequência de exemplo:

UTF-8

UTF-8 usa até 4 bytes para representar pontos de código Unicode. Para o caso de 1 byte, use o seguinte padrão:

UTF-8 de 1 byte = 0xxxxxxxbin = 7 bits = 0-7Fhex

O byte inicial de UTF-8 de 2, 3 e 4 bytes começa com 2, 3 ou 4 um bit, seguido por um bit zero. Os bytes de acompanhamento sempre começam com o padrão de dois bits 10, deixando 6 bits para os dados:

UTF-8 de 2 bytes = 110xxxxx 10xxxxxxbin = 5 + 6 (11) bits = 80-7FFhex
UTF-8 de 3 bytes = 1110xxxx 10xxxxxx 10xxxxxxbin = 4 + 6 + 6 (16) bits = 800-FFFFhex
UTF-8 de 4 bytes = 11110xxx 10xxxxxx 10xxxxxx 10xxxxxxbin = 3 + 6 + 6 + 6 (21) bits = 10000-10FFFFhex

Os pontos de código Unicode são indefinidos além de 10FFFFhex.

Seus pontos de código são U + 006D, U + 0416 e U + 4E3D que requerem sequências UTF-8 de 1, 2 e 3 bytes, respectivamente. Converta em binário e atribua os bits:

U + 006D = 1101101bin = 1101101bin = 6Dhex
U + 0416 = 10000 010110bin = 11010000 10010110bin = D0 96hex
U + 4E3D = 0100 111000 111101bin = 11100100 10111000 10111101bin = E4 B8 BDhex

Sequência final de bytes:

6D D0 96 E4 B8 BD

ou se forem desejadas cadeias terminadas em nulo:

6D D0 96 E4 B8 BD 00

UTF-16

UTF-16 usa 2 ou 4 bytes para representar pontos de código Unicode. Algoritmo:

U + 0000 a U + D7FF usa 0000 de 2 byteshex para D7FFhex
U + D800 a U + DFFF são pontos de código inválidos reservados para UTF-16 de 4 bytes
U + E000 a U + FFFF usa E000 de 2 byteshex para FFFFhex

U + 10000 a U + 10FFFF usa UTF-16 de 4 bytes codificado da seguinte maneira:

  1. Subtrair 10000hex do ponto de código.
  2. Expresse o resultado como binário de 20 bits.
  3. Use o padrão 110110xxxxxxxxxx 110111xxxxxxxxxxbin para codificar os 10 bits superior e inferior em duas palavras de 16 bits.

Usando seus pontos de código:

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

Agora, temos mais um problema. Algumas máquinas armazenam os dois bytes de um byte menos significativo do Word de 16 bits primeiro (as chamadas máquinas little-endian) e algumas armazenam o byte mais significativo primeiro (máquinas big-endian). O UTF-16 usa o ponto de código U + FEFF (chamado marca de ordem dos bytes ou BOM) para ajudar uma máquina a determinar se um fluxo de bytes contém UTF-16 grande ou pequeno:

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

Com terminação nula, U + 0000 = 0000hex:

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

Como seu instrutor não forneceu um ponto de código que exigia UTF-16 de 4 bytes, aqui está um exemplo:

U + 1F031 = 1F031hex - 10000hex = F031hex = 0000111100 0000110001bin =
1101100000111100 1101110000110001bin = D83C DC31hex

39
Mark Tolonen

O programa a seguir fará o trabalho necessário. Pode não ser "manual" o suficiente para seus propósitos, mas você pode, no mínimo, verificar seu trabalho.

#!/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";
4
Seth Robertson