UTF-16
UTF-16 is 16-bit variable lengend encoding scheme and it uses the UTF character set for character code points. Isto significa que um personagem codificado UTF-16 terá uma unidade de código de 16 bits.
Como sabemos que um carácter codificado UTF-8 pode ser representado em 1 a 4 unidades de código, um carácter UTF-16 pode ser representado em 1 ou 2 unidades de código. Portanto, um caráter UTF-16 pode tomar 16 ou 32 bits de memória com base em seu ponto de código.
Antes de saltar para as especificações de codificação UTF-16, vamos entender como podemos fazer o UTF-16 funcionar.
Uma vez que temos unidade de código de 16 bits, em teoria, podemos codificar 21 caracteres⁶ do ponto de código 0 a 65,535. Mas e se tivermos um personagem com um ponto de código superior a 65.535? Nesse caso, podemos adicionar outra unidade de código.
com a unidade de código extra, podemos codificar um total de 232 caracteres que é mais de 4M. mas então a questão é, como um decodificador UTF-16 saberá que ele precisa considerar 2 unidades de código para descodificar um caractere?
UTF-8 resolveu este problema definindo bits iniciais da primeira unidade de código e unidades de código de continuação para alguns valores de bit específicos que um decodificador UTF-8 pode usar para deduzir quantas unidades de código um caractere pode tomar.
Podemos fazer o mesmo com a unidade de código UTF-16, mas então temos que sacrificar alguns bits em uma unidade de código para esta funcionalidade. Podemos definir alguns bits iniciais de uma unidade de código para algum valor significativo que um decodificador UTF-16 pode entender.
também para dar poder de auto-sincronização para unidades de Código, uma unidade de código deve ser capaz de dizer se é a unidade de código inicial ou uma unidade de código de continuação e não um caráter de apenas uma unidade de código.
então Unicode decidiu sacrificar os 6 bits iniciais da unidade de código deixando apenas 10 bits para codificar o ponto de código de um caractere por unidade de código. Se um caractere precisa de 2 unidades de código, 20 bits da memória (de 32 bits ou 4 bytes) contém a informação real do ponto de código do caractere.
então o que são esses bits iniciais e como esses bits fazem uma mossa no conjunto de caracteres UTF? Vamos seguir o exemplo abaixo.
1101 10xx xxxx xxxx 1101 11xx xxxx xxxx
FIRST CODE UNIT---- SECOND CODE UNIT---
a Partir de UTF-16 padrão, a primeira unidade de código deve começar com 110110₂ e a segunda unidade de código deve começar com 110111₂. Isto ajudará um decodificador UTF-16 a entender qual se a primeira unidade de código e qual é a segunda. Isto faz UTF-16 auto-sincronizar.
Agora com o que temos 10 bits por unidade de código para jogar, Qual é o intervalo que podemos jogar dentro? No final, quantos caracteres podem ser codificados em duas unidades de código de codificação UTF-16?
Don’t worry, we will talk about characters encoded in just one code unit.
If you take a look at the above code unit templates, we have a range from 1101 1000 0000 0000₂ to 1101 1111 1111₂. Isso é equivalente a D800₁₆ para dfff₁₆.
💡 A primeira unidade de código tem a escala de D800₁₆ para 6FFF₁₆ e a segunda unidade de código tem a faixa de DC00₁₆ para DFFF₁₆. Podemos obter estes valores ligando todos os bits de pontos de código: ligado e desligado.
Uma vez que UTF-16 tem de ser auto-sincronizado, os pontos de código entre D800₁₆ e dfff₁₆ não devem representar um carácter em UTF-16. Uma vez que todas as codificações UTF seguem o mesmo conjunto de caracteres UTF, estes pontos de código são restritos pelo UTF e eles não são e não serão atribuídos a nenhum caractere⁰.
pontos de Código entre D800₁₆ e DFFF₁₆ não representam quaisquer caracteres, portanto, eles são chamados de substituição de código ou pontos juntos, eles também são chamados como substituto pairs⁰.
o primeiro ponto de código Substituto (a partir da primeira unidade de código) também chamado de alto substituto e segundo ponto de código (a partir da segunda unidade de código) também chamado de baixo substituto. Perfazendo um total de 2048 pontos de código, 1024 por barriga de aluguer.
💁 ♂ substitutos pontos de código sacrificaram suas vidas para que pudéssemos codificar mais personagens com duas unidades de código. Pensa nisso!
então a grande questão, podemos codificar um personagem com uma única unidade de código de UTF-16? A resposta é sim. UTF-16 é um esquema de codificação de 16 bits de comprimento variável. Então isso significa que podemos codificar 21 characters caracteres com uma única unidade de código?a resposta é não. Em teoria, poderíamos codificar 21⁶ caracteres com ponto de código 0000₁₆ (0₁₀) para FFFF₁₆ (65535₁₀), mas os pontos de código entre D800₁₆ e DFFF₁₆ não representam personagens como eles são reservados.
portanto, é seguro codificar caracteres de 0000₁₆ a d7ffff e e000000 a Ffff₁₆, o que corresponde a 63,488 (65536-2048) caracteres. Isto é apenas para os caracteres que podem ser codificados em apenas uma unidade de código de UTF-16.
Uma vez que temos um total de 20 bits para jogar quando se trata de caracteres do que pode ser codificado em 2 unidades de código de UTF-16, podemos codificar 22⁰ mais caracteres, que é 1,048,576 caracteres.
assim, no total, podemos codificar 1,048,576 + 63,488 que equivale a 1,112,064 caracteres (mais de um milhão de caracteres). Este é o limite do conjunto de caracteres UTF. Uma vez que UTF-16 pode codificar esses muitos caracteres, outras codificações UTF não podem representar caracteres além destes.
UTF charset Code Points
As we know that a code point is a decimal value assigned to a character, we (Unicode) must not assign an inválido code point to an actual character. Até agora, os pontos de código inválidos são pontos de código substitutos.
com apenas uma única unidade de código UTF-16, podemos codificar 63.488 caracteres que variam de 0000₁₆ a d7ff₁₆ e e000000 a Ffff₁₆. O último ponto de código é 65.535. Estes são chamados de caracteres BMP (explicados mais tarde).
com duas unidades de código de UTF-16, podemos codificar 1.048.576 caracteres. Uma vez que não podemos começar de novo a partir de 0 Valor (ponto de código), porque estes vêm após os caracteres BMP, precisamos compensá-los em 65,536. Estes caracteres são chamados de caracteres suplementares (explicados mais tarde).
portanto, o primeiro carácter suplementar tem um valor de ponto de código de 65536₁₀, que é equivalente a 10000₁₆. Uma vez que podemos codificar 1.048.576 caracteres com duas unidades de código de UTF-16, o último ponto de código é 1114111₁₀, que é equivalente a 10ffff₁₆.
então vamos dividir as coisas em uma forma tabular simples.
+-----------+---------------------+--------------------+
| UTF-16 CU | Code Point | |
+-----------+---------------------+--------------------+
| 1 | 0000₁₆ - D7FF₁₆ | valid |
+-----------+---------------------+--------------------+
| 1 | D800₁₆ - DFFF₁₆ | invalid(surrogate) |
+-----------+---------------------+--------------------+
| 1 | E000₁₆ - FFFF₁₆ | valid |
+-----------+---------------------+--------------------+
| 2 | 10000₁₆ - 10FFFF₁₆ | valid |
+-----------+---------------------+--------------------+
| | 110000₁₆ - FFFFFF₁₆ | unassigned |
+-----------+---------------------+--------------------+
Com este conhecimento, vamos ver como podemos codificar alguns caracteres em UTF-16. Vamos escolher um simples caractere ASCII de Um (ponto de código: 41₁₆), um caractere de Hindi (Índia) idioma आ (pronunciado como Aa, ponto de código: 906₁₆) e um emoticon 😊 (chamado como rosto Feliz, ponto de código: 1F60A₁₆).
Como podemos ver a partir da tabela acima, tanto A e pode ser codificado em apenas uma unidade de código de UTF-16, uma vez que seus valores são menores que FFFF₁₆.
Quando temos que codificar um caractere em apenas uma unidade de código, apenas temos que converter o ponto de código do caractere em um número binário de 16 bits. Para os caracteres a, 00000000 01000001₂ é a representação UTF-16.do mesmo modo, para o carácter, basta converter o seu ponto de código 906₁₆ para um número binário de 16 bits que é 00001001 00000110₂.normalmente, representamos unidades de código de um carácter em números hexadecimais. Assim, para o caráter a, a representação UTF-16 é 0041₁₆ e similarmente, para o caráter, a representação UTF-16 é 0906₁₆.
para o personagem 😊, as coisas são um pouco diferentes. Seu ponto de código é 1F60A₁₆. Se olharmos para a tabela UTF-16 mencionada acima, ela tem que ser codificada em 2 unidades de código de UTF-16. Então, como começamos?primeiro, precisamos subtrair 10000₁₆ do ponto de código. A razão para isso é, cada caractere codificado em 2 unidades de código de UTF-16 veio após os caracteres BMP cujo último ponto de código é FFFF₁₆.
portanto, para obter o valor real dos bits usados para codificação (que é de 20 em 2 unidades de código), precisamos subtrair 10000₁₆ do ponto de código e usar o número final para gerar esses 20 bits.
💡 To help you understand better, the first character represented with 2 code units of UTF-16 will have all its 20 bits set to 0. Então o valor dos bits usados para codificar o ponto de código deste caractere é 0. Mas ainda assim, seu ponto de código é 10000₁₆ de acordo com o conjunto de caracteres Unicode. Isto porque o valor produzido por estes 20 bits é adicionado a 10000₁₆ para gerar o ponto de código final.
conforme visto anteriormente, estas 2 unidades de código parecem ser as seguintes.
1101 10xx xxxx xxxx 1101 11xx xxxx xxxx
FIRST CODE UNIT---- SECOND CODE UNIT---
só precisamos preencher estes 20 bits (x
) com o valor recebido do ponto de código de caracteres. O ponto de código do caráter 😊é 1F60A₁₆. Mas primeiro, precisamos subtrair 10000₁₆ dele. Temos F60A₁₆.
Agora precisamos converter F60A₁₆ para um número binário de 20 bits e preencher os 20 bits no modelo de unidade de código acima. F60A₁₆ em binário é 0000111101 10000010₂. Agora podemos encher estes 20 pedaços de substituição.
abaixo estão as unidades de código final.
1101 1000 0011 1101 1101 1110 0000 1010
0xD83D 0xDE0A
A maneira rápida de verificar se essas unidades de código, são válidos e, na verdade, se esses pares substitutos, pode representar o caractere codificado, abra um Navegador DevTool e digite console.log('\uD83D\uDE0A');
no console.
Você também pode usar esta ferramenta online para gerar código UTF-16 pontos.planos de caracteres Unicode
um plano é um grupo contínuo de 21⁶ ou 65.536 pontos de código. Uma vez que UTF-16 tem pontos de código restritos a um máximo de 10FFFF₁₆, temos um total de 17 planos de caracteres em Unicode padrão começando de 0 a 16.
Uma vez que 21 characters caracteres podem ser definidos pela unidade de código único de UTF-16 (incluindo pontos de código substitutos), ele forma o primeiro (0th) plano. Este plano contém quase todos os personagens em línguas básicas ao redor do mundo. É por isso que este plano é chamado de Plano Básico multilingue ou BMP.a seguir, temos pontos de código definidos por duas unidades de código de UTF-16. Estes contêm 22 characters caracteres como nós temos 20 bits para codificar o valor do ponto de código. Estes são divididos em 16 planos (2⁴ x 21)). Estes são chamados aviões suplementares.
💡 Para mais informações sobre estes planos, leia este documento da Wikipédia.
a comparação com UCS-2
UCS-2 é uma codificação de 16 bits de Largura Fixa. Isso significa que apenas uma unidade de código de 16 bits é usada para representar um ponto de código. Em teoria, UCS-2 pode representar 21⁶ caracteres distintos, mas há uma torção.
💡 BTW, estamos usando o termo unidade de código nesta codificação de largura fixa para entender a relação entre UTF-16. Na realidade, não existe uma unidade de código em nenhuma codificação fixa.
Desde UCS segue o conjunto de caracteres Unicode, a codificação de caracteres UCS-2 é idêntico para a codificação de caracteres UTF-16, que são representadas em apenas uma unidade de código.
💡 Uma vez que UCS segue o conjunto de caracteres Unicode, não pode codificar um carácter válido com os pontos de código reservados para os substitutos.
So in nutshell, UCS-2 contains the characters of Basic Multilingual Plane. Esta é a razão, alguns documentos antigos, e software usado codificação UCS-2. Mas a codificação UCS-2 tornou-se obsoleta e o UTF-16 é preferido.
Endianness and BOM
tal como discutimos anteriormente, um documento codificado em UTF a baixo nível contém a sequência de unidades de código. Para UTF-8, a unidade de código tem 8 bits de comprimento, enquanto para UTF-16, tem 16 bits de comprimento. Estas unidades de código formam os caracteres.
Um decodificador UTF-8 ou UTF-16 lê as unidades de código sequencialmente, uma unidade de código de cada vez para gerar os caracteres.
cada unidade de código representa um valor numérico que um decodificador UTF-8 ou UTF-16 pode dar uma olhada e decidir se é suficiente para representar um caráter ou se segue outras unidades de código que devem ser consideradas também.
Quando se trata de UTF-8, as coisas são simples. Uma vez que cada unidade de código tem 8 bits de comprimento, converter esse número binário de 8 bits para um valor numérico é rápido e fácil. No entanto, não é o caso da UTF-16.
UTF-16 unidade de código é um número binário de 16 bits (2 bytes) que representa um valor de ponto de código. Gerar o valor numérico de múltiplos bytes, em geral, é complicado e diferentes sistemas se comportam de forma diferente.
Este comportamento depende da endianidade do sistema. A partir de nossa discussão anterior sobre endianness, há duas maneiras de escrever um valor de unidade de código UTF-16. Quer no formato big-endian ou no formato Little-endian.
no formato big-endian, o MSB é armazenado em primeiro lugar e o LSB é armazenado em último lugar. Até agora, estamos escrevendo o valor de unidade de código UTF-16 no formato Big-endian. Para escrever o valor unitário do código UTF-16 em Little-endian, precisamos trocar bytes.vamos falar de carácter. A partir do exemplo anterior, ele pode ser representado em apenas uma unidade de código de UTF-16 e sua codificação em representação hexadecimal parece 0906₁₆.
0906₁₆ é um número de 16 bits com 09 sendo o MSB e 06 sendo o LSB. Assim, na arquitetura big-endian, será armazenado como 09 06. No entanto, em uma arquitetura pouco-endiana, será armazenado como 06 09.
portanto, torna-se nossa responsabilidade codificar caracteres, tomando em mente a endianness do sistema para que o sistema possa ler um documento UTF-16 corretamente.
mas como podemos dizer de antemão se a máquina de um usuário é compatível com o documento codificado ou não? E já que a proximidade de um sistema pode afetar como um documento é decodificado, como podemos compartilhá-lo publicamente?
é aqui que BOM entra na imagem. Uma marca de ordem de Byte (BOM) é uma sequência de byte que é adicionada no início de um arquivo de texto ou dados de texto.
Unicode recomenda caractere com o ponto de código FEFF₁₆ para atuar como um BOM para codificações UTF-16 e UTF-32. Este carácter deve ser anterior ao primeiro carácter do documento. No entanto, este caráter não será considerado pelo decodificador na saída.
Este carácter (U+FEFF) é um carácter sem quebra de largura zero (ZWNBSP) e é invisível. Portanto, mesmo se um decodificador não reconhece o BOM, ele não produz qualquer saída visível.
Este carácter é representado numa única unidade de código de UTF-16 e na representação hexadecimal parece-se com FE (MSB) e FF (LSB).
Assim, quando os caracteres são codificados no formato Big-endian, precisamos adicionar FEFF como o BOM no início do arquivo e quando os caracteres são codificados no formato Little-endian, precisamos adicionar FFFE (inversa) como o BOM no início do arquivo.
Unicode recomenda a adição do BOM ao documento codificado UTF-16. No entanto, se o BOM está faltando então o formato Big-endian é assumido.
IANA prefere UTF-16 como identificador para significar um documento codificado UTF-16. No entanto, UTF-16BE é usado para documentos codificados em formato Big-endian e UTF-16LE é usado para o formato Little-endian.
Quando o nome UTF-16BE ou UTF-16LE é usado, então BOM não é recomendado para ser pré-adicionado a um arquivo. Mesmo neste caso, se o BOM for adicionado, então ele será considerado como o personagem ZWNBSP e não será ignorado.
💡 UTF-16, UTF-16BE e UTF-16LE names are case insensível.
prós e contras
UTF-16 é eficiente porque tem apenas 2 unidades de código e uma vez que a maioria dos caracteres usados caem no conjunto BMP, eles podem ser representados em apenas uma unidade de código. No entanto, vem com muitas questões.
A maior desvantagem do UTF-16 é que ele não é compatível com ASCII. Uma vez que os caracteres ASCII são codificados com uma única unidade de código (número de 16 bits), eles não podem ser descodificados corretamente por um decodificador ASCII.
UTF-16 consome espaço desnecessário para caracteres ASCII. Comparado com o documento codificado UTF-8 que contém apenas caracteres ASCII, o tamanho do mesmo documento codificado em UTF-16 é duas vezes maior.
UTF-16 também é afetado pela endianidade do sistema. Se o BOM estiver em falta e não for usado um identificador de codificação apropriado (como o UTF-16LE), um documento codificado UTF-16 pode não ser descodificado corretamente.
devido à natureza da codificação UTF-16, introduziu pontos de código substitutos que não podem representar os caracteres válidos. Além disso, ele limitou o conjunto de caracteres Unicode para 10FFFF₁₆ (último ponto de código).
apesar destes fatos, algumas das linguagens de programação como JavaScript, Java, etc. e sistemas como o Windows preferem codificação UTF-16.