Diferenças

Aqui você vê as diferenças entre duas revisões dessa página.

Link para esta página de comparações

c:codificacao_de_caracteres [2023/08/01 17:17] – criada mazieroc:codificacao_de_caracteres [2023/08/01 20:13] (atual) – edição externa 127.0.0.1
Linha 1: Linha 1:
 +====== Codificação de caracteres ======
 +
 +{{ progc_strings_codificacao.mkv |Video desta aula}}
 +
 +Internamente, um computador só armazena e processa bytes, números inteiros entre 0 e 255. Não é possível armazenar diretamente textos, imagens, sons ou qualquer outra informação que não sejam bytes.
 +
 +Para armazenar informações mais complexas que os bytes, é necessário **codificar** as mesmas, ou seja, transformá-las em sequências de bytes. Esta página discute as técnicas usadas para transformar as **letras e símbolos de um texto** em bytes, para poder armazená-lo e tratá-lo em um computador.
 +
 +A figura a seguir mostra as etapas do tratamento de uma letra ''A'' pelo computador: o hardware do teclado é responsável por converter a letra digitada em byte(s) na memória do computador; em seguida, o hardware do terminal converte esses bytes em uma representação gráfica na tela.
 +
 +{{ character_encoding.png |Codificação de caracteres}}
 +
 +===== Algumas definições =====
 +
 +  * **Caractere**: é um símbolo da linguagem (letra, dígito ou sinal). Exemplos: ''A i ç ë ¥ β ɣ 诶 😃 ✂''
 +  * **Conjunto de caracteres** (//charset//): é o conjunto de todos os caracteres suportados por um sistema ou por um padrão de codificação. Exemplo: ''A-Z, a-z, 0-9, ! @ # $ % * ( ) ~ _ - + = { } [ ] | \ / < . ,''
 +  * **Codificação** (//encoding//): é a tradução entre os caracteres e seus respectivos valores numéricos em bytes. Exemplo, ''A'' → 65.
 +
 +Existem diversas codificações de caracteres; a seguir serão apresentadas as mais usuais.
 +
 +===== A codificação ASCII =====
 +
 +A codificação de caracteres mais antiga ainda em amplo uso é a [[https://pt.wikipedia.org/wiki/ASCII|ASCII]] (//American Standard Code for Information Interchange//), criada nos anos 1960 a partir de códigos de telegrafia. Sua última atualização ocorreu em 1986, mesmo assim é considerada uma codificação universal.
 +
 +<note important>
 +Praticamente **TODOS** os sistemas computacionais suportam ASCII !
 +</note>
 +
 +A codificação ASCII abrange o conjunto de caracteres da língua inglesa, sinais gráficos e alguns caracteres de controle (nova linha, tabulação, etc), num total de 128 caracteres. Cada caractere é codificado em um byte, mas ocupa somente 7 bits; o oitavo bit de cada byte era antigamente usado para verificação de paridade.
 +
 +A codificação ASCII é definida através da famosa **Tabela ASCII**, que é dividida em duas partes:
 +
 +  * 0 - 31 e 127: caracteres de controle (//newline//, //form feed//, //tab//, etc), que dependem do terminal utilizado.
 +  * 32 - 126: caracteres imprimíveis (A, B, C, ...), independentes de terminal.
 +
 +{{ asciifull.gif |A tabela ASCII}}
 +
 +A codificação ASCII ainda é amplamente usada para codificação de textos puros em inglês, como códigos-fonte de programas, páginas HTML, arquivos de configuração, etc.
 +
 +===== Code pages =====
 +
 +A codificação ASCII não suporta caracteres acentuados (á é ñ ë) ou caracteres específicos de outras línguas, como ç ¥ β ɣ 诶 etc. Pode-se usar o oitavo bit de cada byte para associar caracteres aos valores acima de 127. Isso levou à criação de diversas **tabelas ASCII estendidas** para definir os símbolos de 128 a 255. Cada codificação é denominada uma **code page**; algumas das mais conhecidas são:
 +
 +  * [[https://en.wikipedia.org/wiki/Code_page_437|CP-437]]: (code page 437), codificação usada nos primeiros PCs, com caracteres acentuados e gráficos simples (▒ ╝ ╦ ┼).
 +  * [[https://en.wikipedia.org/wiki/Windows-1252|Windows-1252]]: codificação usada em sistemas Windows mais antigos; é parte de um conjunto de codificações para diversas linguagens chamado [[https://en.wikipedia.org/wiki/Windows_code_page|Windows code pages]].
 +  * [[https://en.wikipedia.org/wiki/KOI8-R|KOI8-R]]: cirílico russo (Код Обмена Информацией, 8 бит).
 +  * [[https://pt.wikipedia.org/wiki/BRASCII|BraSCII]]: português brasileiro, usada nos anos 1980-90.
 +  * [[https://en.wikipedia.org/wiki/ISO/IEC_8859|ISO-8859]] : codificações da ISO para diversas línguas.
 +
 +===== Codificações ISO-8859 =====
 +
 +Nos anos 1980, para para tentar organizar a profusão de codepages ASCII estendidas, a ISO propôs o conjunto de padrões [[https://en.wikipedia.org/wiki/ISO/IEC_8859|ISO-8859]], que define codificações ASCII estendidas para diversas linguagens, como por exemplo:
 +
 +  * [[https://en.wikipedia.org/wiki/ISO/IEC_8859-1|ISO-8859-1]]: Europa ocidental (francês, espanhol, italiano, alemão, etc)
 +  * [[https://en.wikipedia.org/wiki/ISO/IEC_8859-15|ISO-8859-15]]: revisão do ISO-8859-1, contendo o € e outros símbolos
 +  * [[https://en.wikipedia.org/wiki/ISO/IEC_8859-2|ISO-8859-2]]: Europa central (Bósnio, Polonês, Croata, etc)
 +  * [[https://en.wikipedia.org/wiki/ISO/IEC_8859-6|ISO-8859-6]]: árabe simplificado
 +  * [[https://en.wikipedia.org/wiki/ISO/IEC_8859-7|ISO-8859-7]]: grego
 +
 +As codificações ISO-8859 se tornaram um padrão mundial e ainda são amplamente usadas em muitos sistemas, sendo gradualmente substituída pela codificação Unicode em sistemas mais recentes. Elas são compatíveis com a codificação ASCII, pois representam cada caractere com **somente um byte** e respeitam as definições ASCII dos caracteres de 0 a 127.
 +
 +<note important>
 +Programas que manipulem caracteres ISO devem usar variáveis ''unsigned char'', para poder representar valores de o a 255.
 +</note>
 +
 +===== Caracteres multibyte =====
 +
 +O maior problema das codificações ISO-8859 é o uso de somente **um byte por caractere**, o que limita cada //code page// a 256 caracteres. Essa limitação impede a representação completa de línguas asiáticas e do árabe, por exemplo.
 +
 +Para representar conjuntos com mais de 256 caracteres é necessário usar **caracteres multibyte**, ou seja, com mais de um byte. Por exemplo, se usarmos 2 bytes por caractere é possível representar até 2<sup>16</sup> = 65.536 caracteres distintos na mesma tabela, sem precisar trocar de //code page//.
 +
 +Vários padrões de codificação multibyte foram propostos, como:
 +
 +  * [[https://en.wikipedia.org/wiki/ISO/IEC_2022|ISO-2022-CJK]]: chinês, japonês, coreano
 +  * [[https://en.wikipedia.org/wiki/Shift_JIS|Shift-JIS]]: japonês (Windows)
 +  * [[https://en.wikipedia.org/wiki/GB_18030|GB 18030]]: padrão oficial chinês
 +  * [[https://en.wikipedia.org/wiki/Big5|Big5]]: chinês tradicional (Taiwan)
 +  * **Unicode**
 +
 +Alguns destes padrões definem todos os caracteres com uma quantidade fixa de bits (16 ou 32), enquanto outros definem caracteres com tamanho variável (8, 16 ou 32 bits).
 +
 +===== Unicode =====
 +
 +O padrão [[https://en.wikipedia.org/wiki/Unicode|Unicode]] define um imenso conjunto de caracteres e os modos de codificação dos mesmos. Atualmente, existem cerca de [[https://unicode-table.com/en/|140.000 caracteres]] definidos em Unicode, para todas as línguas conhecidas (inclusive [[https://en.wikipedia.org/wiki/Klingon_scripts|Klingon]]!), além de símbolos e emojis. Eles ocupam pouco mais de 10% da capacidade total desse padrão.
 +
 +Em Unicode, cada caractere possui um código numérico único, chamado //code point//, que pode ser representado de diversas formas. Por exemplo, o //code point// do emoji 😜 vale 128540 (1F61C<sub>h</sub>) e pode ser representado como:
 +
 +  * ''U+1f61c'' : em hexadecimal
 +  * ''&#x1f61c;'' ou ''&#128540;'' : em páginas Web (decimal ou hexadecimal) 
 +  * ''\u1f61c'' : em algumas linguagens de programação
 +
 +Caracteres em Unicode podem ser codificados (representados em bytes) de diversas formas:
 +
 +  * [[https://en.wikipedia.org/wiki/UTF-8|UTF-8]]: //8-bit Unicode Transformation Format//, usa de 1 a 4 bytes por caractere. É usado no Linux, Windows 10 e outros sistemas recentes.
 +  * [[https://en.wikipedia.org/wiki/UTF-16|UTF-16]]: usa 2 ou 4 bytes por caractere; muito usado nas APIs dos sistemas Windows, em Java, Python e PHP.
 +  * [[https://en.wikipedia.org/wiki/UTF-32|UTF-32]]: usa sempre 4 bytes por caractere. É pouco usado na prática.
 +
 +===== A codificação UTF-8 =====
 +
 +UTF-8 é certamente a codificação multibyte [[https://w3techs.com/technologies/history_overview/character_encoding/ms/y|mais utilizada hoje em dia]], por ser plenamente compatível com a codificação ASCII e por ser econômica em espaço.
 +
 +Em UTF-8, cada caractere Unicode é codificado usando de 1 a 4 bytes, conforme o número de bits de seu //code point//:
 +
 +^ Caractere ^ Code point ^  Em binário  ^ bits ^
 +|  A  | 41<sub>h</sub> (65) |  ''100 0001'' |  7  |
 +|  ç  | E7<sub>h</sub> |  ''1110 0111'' |  8  |
 +|  ©  | C2A9<sub>h</sub> |  ''1100 0010 1010 1001'' |  16  |
 +|  😀  | 1F600<sub>h</sub> |  ''1 1111 0110 0000 0000'' |  17  |
 +
 +A regra de codificação de cada caractere é escolhida conforme o número de bits usados pelo seu //code point//:
 +
 +|< 100% 15% 55% >|
 +^ # de bits do caractere ^ formato codificado ^ bytes ^ Uso ^
 +| até 7 bits  | ''0xxx-xxxx'' |  1  | tabela ASCII |
 +| 8-11 bits | ''110x-xxxx 10xx-xxxx'' |  2  | caracteres estendidos |
 +| 12-16 bits | ''1110-xxxx 10xx-xxxx 10xx-xxxx'' |  3  | caracteres estendidos |
 +| 17-21 bits | ''1111-0xxx 10xx-xxxx 10xx-xxxx 10xx-xxxx'' |  4  | caracteres estendidos |
 +
 +Pode-se observar que bytes os bytes iniciando em ''0...'' sempre representam caracteres ASCII. Então, um texto codificado em UTF-8 contendo somente caracteres com códigos entre 0 e 127 equivale a um texto codificado em ASCII padrão.
 +
 +Além disso, todos os bytes iniciando em ''10...'' são bytes de continuação da codificação de um caractere multibyte. Isso significa que é fácil localizar o início de cada caractere no texto, mesmo na presença de erros.
 +
 +<note tip>
 +**Dica**: pode-se visualizar o conteúdo de um arquivo em hexadecimal ou binário usando o comando ''xxd''.
 +</note>
 +
 +O mecanismo de codificação de //code points// Unicode em UTF-8 funciona da seguinte forma:
 +
 +  - Dado um caractere, verifica-se quantos bits são necessários para armazenar seu código em UTF-8. Por exemplo, o caractere 😀 (//code point// ''U+1f600'') precisa de 17 bits: 1F600 → 1 1111 0110 0000 0000.
 +  - Para 17 bits é necessário codificar usando 4 bytes (faixa 17-21 bits)
 +  - Distribui-se os bits do código numérico do caractere nos espaços disponíveis:<code>
 +Code point (hex)                        f    6    0    0
 +Code point (bin)                     0001 1111 0110 0000 0000
 +Encoding format       1111-0xxx 10xx-xxxx 10xx-xxxx 10xx-xxxx
 +Code point (bin)            000   01 1111   01 1000   00 0000
 +Encoded character     1111 0000 1001 1111 1001 1000 1000 0000
 +                      f    0    9    f    9    8    8    0</code>
 +  - Com isso, a codificação de U+1f600 em UTF-8 resulta nos 4 bytes ''f0 9f 98 80''.
 +  - A decodificação (de UTF-8 para o //code point//) se efetua fazendo o caminho inverso.
 +
 +Alguns arquivos codificados em UTF-* podem apresentar em seus dois primeiros bytes um valor chamado BOM (//Byte Order Mark//), que define em que ordem os bytes de cada caractere devem ser considerados: //big endian// ou //little endian//. o campo BOM não é necessário em UTF-8, mas pode estar presente às vezes: 
 +
 +^ Bytes ^ Encoding Form ^
 +| ''00 00 FE FF'' | UTF-32, big-endian |
 +| ''FF FE 00 00'' | UTF-32, little-endian |
 +| ''FE FF''       | UTF-16, big-endian |
 +| ''FF FE''       | UTF-16, little-endian |
 +| ''EF BB BF''    | UTF-8 |
 +
 +<note tip>
 +**Dica**: no Linux, pode-se digitar caracteres Unicode usando as seguintes teclas: ''ctrl'' + ''shift'' + ''u'',  código hexadecimal, ''enter''
 +</note>
 +
 +===== Comparando as codificações =====
 +
 +O quadro a seguir compara a representação da string "equação" usando algumas das codificações estudadas. No caso da codificação ASCII, considera-se a letra sem acento ou cedilha; a representação UTF-16 usa dois bytes de cabeçalho BOM (//Byte Order Mark//).
 +
 +^ Codificação ^  BOM  ^  e  ^  q  ^  u  ^  a  ^  ç  ^  ã  ^  o  ^  ^
 +^ ASCII             | ''65'' | ''71'' | ''75'' | ''61'' | ''63'' | ''61''    | ''6f'' | ''00'' |
 +^ ISO-8859-1  |       | ''65'' | ''71'' | ''75'' | ''61'' | ''e7'' | ''e3''    | ''6f'' | ''00'' |
 +^ UTF-8             | ''65'' | ''71'' | ''75'' | ''61'' | ''c3 a7'' | ''c3 a3'' | ''6f'' | ''00'' |
 +^ UTF-16 (be) | ''fe ff'' | ''00 65'' | ''00 71'' | ''00 75'' | ''00 61'' | ''00 e7'' | ''00 e3'' | ''00 6f'' | ''00 00'' |
 +
 +===== Conversão de codificações =====
 +
 +O comando ''file'' do UNIX informa o tipo de codificação usado em um arquivo de texto:
 +
 +<code>
 +$ file exemplo.*
 +exemplo.c:    C source, ISO-8859 text
 +exemplo.html: HTML document, ASCII text
 +exemplo.txt:  UTF-8 Unicode text
 +</code>
 +
 +A conversão de codificação de um arquivo de texto pode ser feita com utilitários específicos, como o ''iconv'' no Linux:
 +
 +<code shell>
 +iconv -f ISO-8859-15 -t UTF-8 < input.txt > output.txt
 +</code>
 +
 +Além disso, os editores de texto geralmente permitem escolher a codificação ao salvar o arquivo. Por exemplo, no VI:
 +
 +<code vi>
 +:set fileencoding=utf8
 +:w myfilename
 +</code>
 +
 +===== Mais informações =====
 +
 +Sobre codificações e Unicode:
 +
 +  * https://www.cl.cam.ac.uk/~mgk25/unicode.html
 +  * https://www.ime.usp.br/~pf/algoritmos/apend/unicode.html
 +  * https://www.cprogramming.com/tutorial/unicode.html
 +  * https://begriffs.com/posts/2019-05-23-unicode-icu.html
 +  * http://kunststube.net/encoding/
 +
 +===== Exercícios =====
 +
 +  - Use o programas ''file'' e ''iconv'' para fazer as seguintes conversões:
 +    - o arquivo {{exemplo.c |}} para UTF-8
 +    - o arquivo {{exemplo.c |}} para ASCII