Minicurso de Encoding André Willik Valenti (Daitan Group) Andrei Tognolo (Dextra Sistemas) The Developer’s Conference Goiânia 2012
Dec 05, 2014
Minicurso de Encoding
André Willik Valenti (Daitan Group)Andrei Tognolo (Dextra Sistemas)
The Developer’s ConferenceGoiânia 2012
Roteiro▒ Parte 1
▒ O que é e por que existe
▒ Parte 2▒ Como funciona▒ Por que dá errado
▒ Parte 3▒ Como faz pra dar certo
Parte 1
O que é e por que existe
Quem é o encoding?▒ O encoding é o culpado por:
▒ Programação Programação Programa褯
▒ José Luís Assunção Júnior José Luà s Assunção Júnior Jos題 uAssun 褯 Jr
Quem é o encoding?▒ Mais especificamente:
▒ Character encoding
▒ Também conhecido como:▒ Codificação de caracteres▒ Charset
Por que encoding?▒ Se dá tanto problema...
▒ Por que a gente usa?
Por que encoding?▒ Computador é uma máquina de armazenar e processar informação
▒ Informação é um conceito abstrato
▒ Computador não entende conceitos abstratos
▒ Computador entende de bits, bytes, números
Por que encoding?▒ Exemplo: imagens
Por que encoding?▒ Como a gente representa uma imagem num computador?▒ Imagem pixels▒ pixels números▒ números bytes/bits▒ bytes/bits algum formato
(png, jpeg, gif etc.)
PNG
100011000100100100...
(440KB)
00110001011...
(62KB)
JPEG
Por que encoding?▒ A imagem é uma informação
▒ Bytes / bits são dados
▒ Dados são concretos, informação é abstrata
▒ Informação = dados + forma de interpretá-los
Informação=
dados + formade interpretá-los
Encoding▒ “Forma de interpretá-los”
▒ Isso é o encoding!
Encoding▒ Exemplos de encoding:
▒ PNG▒ JPEG▒ MPEG▒ MP3▒ PDF
Character encoding▒ O que é então um character encoding?▒ É só uma determinada maneira de se representar caracteres usando bytes
Encoding▒ Por que a gente usa encoding, então?▒ Porque não tem como não usar!
Parte 2
Como (não) funciona
Por que dá errado
História▒ Década de 60
▒ Mais de 60 maneiras diferentes de representar caracteres
▒ Cada fabricante implementava do seu jeito
▒ Bob Bemer:▒ “Vamos uniformizar esse negócio...”
ASCII▒ American Standard Code for Information Interchange
▒ 7 bits
▒ 128 diferentes caracteres
ASCII▒ ASCII é, ao mesmo tempo:
▒ Um encoding▒ Uma tabela de caracteres
ASCII▒ Exemplos:
Caractere Decimal Hexa Binário
5 53 0x35 0110101
A 65 0x41 1000001
} 125 0x7D 1111101
ASCII▒ 128 caracteres (nenhum acentuado)
▒ Intervalo válido▒ Em decimal: 0 – 127▒ Em binário: 0000000 – 1111111▒ Em hexadecimal: 0x00 – 0x7F
ASCII▒ 7 bits?
▒ Muitas máquinas usavam/usam o padrão de 8 bits para 1 byte
▒ E esse bit sobrando aí?▒ “Já sei... Vamos usar pra codificar caracteres locais de cada país!”
ASCII estendido▒ 8 bits: 256 caracteres
▒ Intervalo válido:▒ 0 – 255 (em decimal)▒ 0x00 – 0xFF (em hexadecimal)
ASCII estendido▒ 0x00 – 0x7F: igual ASCII
▒ a b C D 3 5 9 % & /
▒ 0x80 – 0xFF: caracteres locais▒ ç Á ã é ü ¿
0x80 0x81 0x82 ... 0xFF
ASCII original
ASCII estendido
0x00 0x01 0x02 ... 0x7F
ASCII estendido▒ Problema:
▒ Não existe “o encoding ASCII estendido”
▒ Existem UM MONTE de encodings que são “ASCII estendido”
ASCIIs estendidos▒ Codepages
▒ 437 — The original IBM PC code page▒ 720 — Arabic▒ 737 — Greek▒ 775 — Estonian, Lithuanian and Latvian▒ 850 — "Multilingual (Latin-1)"▒ 852 — "Slavic (Latin-2)"▒ 855 — Cyrillic▒ 857 — Turkish▒ 858 — "Multilingual" with euro symbol▒ 860 — Portuguese▒ 863 — French (Quebec French) ▒ 865 — Danish/Norwegian▒ 866 — Cyrillic ▒ 869 — Greek ▒ 874 — Thai▒ ...
ASCIIs estendidos▒ ISO-8859-1
▒ Também conhecido como LATIN-1
▒ É um ASCII estendido:▒ 256 caracteres▒ Compatível com ASCII
▒ Usado até hoje
ASCIIs estendidos▒ Maravilha!
▒ Milhares de encodings diferentes
▒ Agora todo mundo vai conseguir representar seus caracteres!
ASCIIs estendidos▒ E a interoperabilidade?
▒ Internet, internacionalização?▒ Troca de textos, documentos?▒ Nomes de arquivos?
Júnior.txt
Olá, meu nome é José Luàs de
Assunção Júnior, sou irmão da SÃ
lvia, da Cláudia e do João.
ASCII▒ No fim das contas, ficou assim:
ASCII▒ Na teoria:
▒ ASCII original: 0x00 – 0x7F
▒ Extensão do ASCII: 0x80 – 0xFF
ASCII▒ Na prática:
▒ ASCII original: Blz!
▒ Extensão do ASCII: Esquece!
Exemplo▒ Você, aqui no Brasil, usando o DOS, gostaria de dar o seu olá
▒ Você escreve:▒ Ola!
▒ No Brasil, a gente usava o encoding chamado “Codepage 850” (ou CP850)
Exemplo▒ Essa sua sequência de 4 caracteres (“Ola!”) é uma informação
▒ Informação só existe na cabeça dos seres humanos
▒ Computador não conhece informação. Computador conhece dados.
Exemplo▒ Para um computador, não existe:
▒ Ola!
▒ O que existe são estes 4 bytes:▒ 0x4F 0x6C 0x61 0x21
▒ Resultado:
Exemplo▒ Blz !
▒ O l a !▒ ↑ ↑ ↑ ↑▒ 0x4F 0x6C 0x61 0x21
Exemplo▒ Mensagem foi codificada em CP850
▒ Todos os caracteres eram ASCII também
▒ Na prática:▒ A mensagem está em ASCII▒ Todo computador entende ASCII▒ Então blz!
Exemplo▒ E se a gente escrever...
▒ Olá!
▒ Resultado:
Exemplo▒ Blz !
▒ O l á !▒ ↑ ↑ ↑ ↑▒ 0x4F 0x6C 0xE1 0x21
▒ (Usando codepage 850)
Exemplo▒ Até você tentar ler isso num computador russo e...
Exemplo▒ Não blz
▒ O l р !▒ ↑ ↑ ↑ ↑▒ 0x4F 0x6C 0xE1 0x21
Exemplo▒ Encoding russo disse:
▒ 0xE1 é um р
▒ Encoding brasileiro tinha dito:▒ 0xE1 é um á
▒ Quem está certo?
Encodings▒ Não existe uma forma única de representar o caractere á
▒ A sequência de bytes é ambígua:▒ 0x4F 0x6C 0xE1 0x21
Encodings▒ E se tivesse um jeito...
▒ ...de informar ao computador russo que a gente usou o CP850?
▒ Segure essa ideia!
Encodings▒ O mundo inteiro...
▒ ...cada um com o seu encoding?
▒ É óbvio que não daria certo▒ Teria que uniformizar de verdade!
Encodings▒ E se houvesse...
▒ Um código...▒ Um código único...▒ Um único código...▒ Um...
UNICODE!!
Unicode▒ O que é Unicode?
The Unicode 5.0 Standard
▒ 1472 páginas▒ É grande
O que é Unicode?▒ Unicode é:
▒ Um padrão gigantesco▒ Subdividido em muitas partes
▒ Unicode, ao contrário do ASCII:▒ Não É uma tabela de caracteres▒ Não É um encoding
O que é Unicode?▒ Unicode TEM uma tabela de caracteres:▒ UCS: Universal Character Set
▒ Unicode TEM diversos encodings▒ UTF-8, UTF-16, UTF-32 (Unicode Transformation Format)
UCS▒ Tabela gigante de caracteres (~100.000)
▒ Cada caractere possui um código, chamado code point▒ Code point é representado por U+ e um número em hexadecimal
UCS▒ Exemplos:
▒ U+0058: X▒ U+00E3: ã▒ U+2603: ☃▒ U+10123:
UCS▒ Intervalo U+0000 – U+007F
▒ Mesmos caracteres da tabela ASCII
UCS▒ Caracteres na tabela são abstratos (são informação)
▒ Para concretizá-los, é necessário um encoding
Encodings Unicode▒ Maneiras de transformar caracteres abstratos em concretos
▒ Três principais: UTF-8, UTF-16, UTF-32
UTF-32▒ Exemplos:
▒ U+0058: 0x00 0x00 0x00 0x58▒ U+00E3: 0x00 0x00 0x00 0xE3▒ U+2603: 0x00 0x00 0x26 0x03▒ U+10123: 0x00 0x01 0x01 0x23
UTF-16▒ Exemplos:
▒ U+0058: 0x00 0x58▒ U+00E3: 0x00 0xE3▒ U+2603: 0x26 0x03▒ U+10123: 0xD8 0x00 0xDD 0x23
UTF-8▒ Exemplos:
▒ U+0058: 0x58▒ U+00E3: 0xC3 0xA3▒ U+2603: 0xE2 0x98 0x83▒ U+10123: 0xF0 0x90 0x84 0xA3
UTF-8▒ Compatível com ASCII entre U+0000 e U+007F
▒ A partir de U+0080, usa mais de 1 byte
▒ Para os caracteres da língua portuguesa, usa 1 ou 2 bytes
UCS e UTF▒ O que eu preciso saber disso tudo?
▒ Apenas o seguinte:
UCS e UTF▒ Tabela de caracteres ≠ encoding
▒ Diferentes de representação▒ Mais comum: UTF-8
▒ UTF-8▒ Número variável de bytes por caractere (em geral, 1 ou 2)
No mundo real de hoje▒ Encodings mais usados nos sistemas que rodam no Brasil:▒ UTF-8▒ LATIN-1
No mundo real de hoje▒ U+0000 – U+007F
▒ Mesmos caracteres da tabela ASCII
▒ U+0000 – U+00FF▒ Mesmos caracteres da tabela LATIN-1
LATIN-1▒ Serve para representar qualquer caractere Unicode?
LATIN-1▒ Não!
▒ Usa exatamente 1 byte por caractere▒ 1 byte não seria suficiente
LATIN-1 e UTF-8▒ Exemplos
CaractereBytes usando
UTF-8Bytes usando
LATIN-1
X 0x58 0x58
à 0xC3 0xA3 0xE3
☃ 0xE2 0x98 0x83 Não existe
LATIN-1 e UTF-8▒ Blz!
▒ Então vamos representar a string “José”
▒ String: é uma informação ou um dado?
LATIN-1 e UTF-8▒ Lembra o Barakis Obamis com o PNG e o JPEG?
PNG
100011000100100100...
(440KB)
00110001011...
(62KB)
JPEG
LATIN-1 e UTF-8▒ O José com o LATIN-1 e UTF-8 é a mesma coisa!
LATIN-1
0x4A 0x6F 0x73 0xE9
(4 bytes)
UTF-8
José
0x4A 0x6F 0x73 0xC3 0xA9
(5 bytes)
LATIN-1 e UTF-8▒ Blz. Mas por que eu escrevo “José” num lugar e depois aparece “José” no outro?
LATIN-1 e UTF-8▒ Codificando “José” em UTF-8:
▒ J o s é▒ ↑ ↑ ↑ ↑▒ 0x4A 0x6F 0x73 0xC3 0xA9
LATIN-1 e UTF-8▒ Aí você envia esses bytes
0x4A 0x6F 0x73 0xC3 0xA9
para alguém
LATIN-1 e UTF-8▒ Se a pessoa ler em UTF-8, blz!
▒ 0x4A 0x6F 0x73 0xC3 0xA9
↓ ↓ ↓ ↓ J o s é
LATIN-1 e UTF-8▒ Mas e se ela ler em LATIN-1?
▒ 0x4A 0x6F 0x73 0xC3 0xA9
↓ ↓ ↓ ↓ ↓ J o s à ©
LATIN-1 e UTF-8▒ O texto foi:
▒ Escrito em UTF-8▒ Enviado a outro sistema
▒ Como o outro sistema vai adivinhar que o texto está em UTF-8?
Unicode▒ Mas então não é só todo mundo codificar seus caracteres usando Unicode e vai dar tudo certo!?
Unicode▒ Calma ...
▒ Em 1º lugar, Unicode não é um encoding. Não existe “codificar caracteres usando Unicode”.
▒ Em 2º lugar, não temos controle sobre todos os sistemas do mundo. Não podemos fazer com que todos usem o mesmo encoding (nem deveríamos).
Parte 3
Como faz pra dar certo
Informação=
dados + formade interpretá-los
Informação String
= =
Dados Bytes
+ +
Forma de interpretá-los
Encoding
String..
=..bytes + encoding
Não existe string sem encoding!
Não existe relação byte caractere sem encoding!
Como faz pra dar certo▒ 2 dicas para evitar problemas de encoding:
Como faz pra dar certo▒ 1) Use sequências de escape sempre que possível
▒ Para escrever “Programação”:▒ Em Java:
▒"Programa\u00e7\u00e3o"▒ Em HTML:
▒<p>Programação</p>
Como faz pra dar certo▒ 2) Faça conversões só quando realmente for necessário
▒ Ao fazer qualquer conversão, SEMPRE especifique o encoding
Como faz pra dar certo▒ Vetor de bytes não é string!▒ String não é vetor de bytes!
▒ O que é válido é:▒ Decompor string em vetor de bytes + encoding
▒ Compor string a partir de um vetor de bytes + um encoding
Como faz pra dar certo▒ Quando é que realmente precisamos fazer conversões?▒ Quando fazemos entrada/saída
Como faz pra dar certo▒ Exemplos:
Como faz pra dar certo▒ Você vai enviar texto em uma requisição HTTP?▒ Converta a string para bytes usando algum encoding
▒ Avise ao servidor que você vai usar esse encoding
▒ Envie os bytes
Como faz pra dar certo▒ Errado:
▒ Content-Type: text/plain
▒ Certo:▒ Content-Type: text/plain; charset=utf-8
▒ Content-Type: text/plain; charset=iso-8859-1
Como faz pra dar certo▒ Vai receber um XML?
▒ Receba o conteúdo (bytes)▒ Repasse diretamente os bytes para a sua biblioteca de processamento de XML
Como faz pra dar certo▒ Não preciso me preocupar com encoding ao processar XML?▒ Em geral, não!▒ XML informa seu encoding dentro do próprio documento▒ <?xml version="1.0" encoding="utf-8"?>▒ (deveria, pelo menos!)
Como faz pra dar certo▒ “Tem que passar o encoding, tem que passar o encoding...”
▒ E se eu não passar o encoding? Não funciona?
Como faz pra dar certo▒ Funciona!
▒ ...às vezes!
▒ O que acontece se eu não especificar encoding?▒ Nenhum encoding será usado!▒ Certo!?
!!!!!!!!!!!
!!ERRADO!!
!!!!!!!!!!!
Como faz pra dar certo▒ Se você não especificar encoding, será usado o encoding padrão da plataforma
▒ E isso é ERRADO!▒ É um perigo!▒ É um absurdo!
Como faz pra dar certo▒ Plataformas MUDAM!▒ Configurações de ambiente MUDAM!▒ Encoding padrão MUDA!
Java: jeito errado▒ byte[] meusBytes =
string.getBytes();
(...)
String minhaString =new String(bytes);
▒ Está dependente de plataforma!
Java: jeito certo▒ byte[] meusBytes =
string.getBytes("UTF-8");
(...)
String minhaString =new String(bytes, "UTF-8");
▒ Agora, sim, independe de plataforma!
Java: jeito certo▒ Não precisa ser UTF-8, pode ser qualquer outro
Java: jeito certo▒ byte[] meusBytes =
string.getBytes("LATIN1");
(...)
String minhaString =new String(bytes,
"LATIN1");
Java: jeito certo▒ byte[] meusBytes =
string.getBytes("UTF-16");
(...)
String minhaString =new String(bytes, "UTF-
16");
Java: jeito certo▒ byte[] meusBytes =
string.getBytes("UTF-32");
(...)
String minhaString =new String(bytes, "UTF-
32");
Demonstração...
Conclusões▒ Problemas de encoding acontecem nas melhores famílias
Conclusões▒ Causas são sempre as mesmas:
▒ String sendo lida e/ou escrita usando o encoding errado
▒ Uso indevido do encoding padrão da plataforma
▒ Causa raiz de todo o problema:▒ Ambiguidade: mais de uma maneira de representar a mesma informação
Conclusões▒ A solução é:
▒ Lembrar: não existe string sem encoding!
▒ Informar o encoding toda vez que fizer entrada/saída
Referências▒ The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)▒ http://www.joelonsoftware.com/articles/Unicode.html
Referências▒ Lista de caracteres Unicode e suas diferentes representações▒ http://www.fileformat.info/info/unicode/
▒ The Unicode Consortium▒ http://unicode.org/
Referências▒ ASCII
▒ http://en.wikipedia.org/wiki/ASCII
▒ Unicode▒ http://en.wikipedia.org/wiki/Unicode
▒ UTF-8▒ http://en.wikipedia.org/wiki/UTF-8
Adendo▒ “José” em UTF-8 gasta 8 bytes?
▒ http://en.wikipedia.org/wiki/Byte_order_mark
▒ Encoding padrão da plataforma?▒ Em geral, é errado usar▒ Em raras situações, é correto. Ex: upload de arquivo no cliente.
Contatos▒ Andr\u00e9 Willik Valenti
▒ [email protected]▒ @awvFi
▒ Andrei de Oliveira Tognolo▒ [email protected]▒ /andreitognolo