[LL] UTF-8: você usa mais do que você pensa
Quem nunca se deparou com algum desses símbolos em algum momento? Isso acontece por causa que o que você recebeu não conseguediu ser encodificado de volta para o formato que o aplicativo ou aparelho que você está usando utiliza, e geralmente fazer uma atualização já resolve seu problema.
No mundo da programação, a gente usa codificação o tempo todo e talvez nem se dê conta disso. A maioria das linguages de programação atuais usam UTF-8 como forma de codificação padrão, algumas já estão usando até mesmo UTF-16, como Dart, e as que não usam, cogitam fazer essa mudança, como Java. O VSCode, amado por muitos, e por outros nem tanto (comigo incluso), mostra para você que tipo de codificação o arquivo atual está usando. Mas você já parou para se perguntar como o UTF-8 funciona por baixo dos panos?
Motivação
Por ser meu primeiro post aqui, irei fazer uma breve explicação dos meus sentimentos atuais como aspirante de desenvolver no Brasil. Sinta-se a vontade se quiser pular esse trecho.
Atualmente curso Engenharia da Computação e para todo lado que eu olho em minha volta eu vejo desenvolvimento web. Não me entenda mal, acho super válido criação de conteúdo desse assunto, mas gostaria que outros também tivessem maior notoriedade. No exterior é algo mais disseminado, mas como eu ainda não estou lá, vou tentar trazer um pouco de conteúdo sobre programação de mais baixo nível por aqui e quem saber dispertar a curiosidade de outras pessoas.
Entretanto, por ser estudante, ainda não tenho tanto conhecimento de low-level (LL do título) quanto a programação em si, só teoria, então vou aproveitar a oportunidade também para aprender com a turma.
UTF-8
Nesse post ficarei focado somente no funcionamento da encodificação e decodificação do UTF-8, mas se você tiver curiosidade sobre a história, significado do acrónimo e coisas assim, aqui vai o link que eu usei quando estava estudando.
Encodificação
O formato de um código de caracter Unicode é algo como isso: U+20AC. Repare bem que depois do prefixo U+ é um número hexadecimal, e é justamente com ele que o trabalho é feito. Mas não é tão simples assim, já que existem intervalos de código que exigem um processo de encodificação diferente, apesar de bem similares.
Os exemplos a seguir levam em consideração que você vai receber como entrada o próprio número hexadecimal.
U+0000 - U+007F
Na realidade, nesse primeiro intervalo, o valor encodificado e decodificado é o mesmo. Bom, tecnicamente falando, deve-se adicionar um 0 no ínicio do binário, mas não é necessário pois 7 é 0b0111
. Agora vamos para o próximo intervalo.
U+0080 - U+07FF
Irei exemplificar esse intervalo usando U+0418.
- Transforma-se o valor hexadecimal em binário
0x418 = 0b100_0001_1000
- Separe os primeiros 5 bits mais significativos dos 6 menos significativos
byte1 = 0b1_0000
byte2 = 0b01_1000
- Adicione
0b110
ao byte1, e0b10
ao byte2, como bits mais significativos
byte1: 0b1_0000 => 0b1101_0000
byte2: 0b01_1000 => 0b1001_1000
- Junte os bytes em ordem crescente, byte1 primeiro e byte2 depois
byte1 byte2
result = 0b1101_0000_1001_1000
- Pronto, agora temos
0x418
encodificado em UTF-8, resultando em0xD098
U+0800 - U+FFFF
Como falei anteriormente, o processo de encodificação é bem parecido, então reaproveitarei da explicação do intervalo anterior no que for possível. Aqui o exemplo em questão será U+20AC.
- Mesmo processo
- Separe em bytes em intervalos de 4-6-6 bits
byte1 = 0b0010
byte2 = 0b00_0010
byte3 = 0b10_1100
- Adicione
0b1110
ao byte1, e0b10
a byte2 e byte3
byte1: 0b0010 => 0b1110_0010
byte2: 0b00_0010 => 0b1000_0010
byte3: 0b10_1100 => 0b1010_1100
- Mesmo processo
- Resultado
0xE2_82_AC
U+10000 - U+10FFFF
Este é o último intervalo, sendo assim, valores maiores que U+10FFFF não existem em UTF-8. O exemplo que irei utilizar aqui é U+10348.
- Mesmo processo
- Separe em bytes de intervalos de 3-6-6-6 bits
- Adicione
0b1_1110
ao byte1, e0b10
aos bytes2-4 - Mesmo processo
- Resultado
0xF0_90_8D_88
Tabela
Se você é como eu, que gosta de algo mais visual, aqui vai uma tabela resumão para te ajudar.
Intervalo | Byte 1 | Byte 2 | Byte 3 | Byte 4 |
---|---|---|---|---|
U+0000 - U+007F | 0xxxxxxx | |||
U+0080 - U+07FF | 110xxxxx | 10xxxxxx | ||
U+0800 - U+FFFF | 1110xxxx | 10xxxxxx | 10xxxxxx | |
U+10000 - U+10FFFF | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |
Decodificação
Entendendo o processo de encodificação, a decodificação de volta para Unicode é literalmente o processo contrário, nesse caso de UTF-8, a única coisa a se atentar é se a entrada é válida.
Repositório e Feedback
Espero que tenham gostado do post, turma. Por favor, deixem seus feedbacks no comentários dizendo o que posso melhorar para os próximos. Eu também fiz um simples encodificador e decodificador de UTF-8 em C, você pode encontrar ele aqui, e também estou aberto a feedbacks em relação ao código.