====== 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. Praticamente **TODOS** os sistemas computacionais suportam ASCII ! 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. Programas que manipulem caracteres ISO devem usar variáveis ''unsigned char'', para poder representar valores de o a 255. ===== 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é 216 = 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 (1F61Ch) e pode ser representado como: * ''U+1f61c'' : em hexadecimal * ''😜'' ou ''😜'' : 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 | 41h (65) | ''100 0001'' | 7 | | ç | E7h | ''1110 0111'' | 8 | | © | C2A9h | ''1100 0010 1010 1001'' | 16 | | 😀 | 1F600h | ''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. **Dica**: pode-se visualizar o conteúdo de um arquivo em hexadecimal ou binário usando o comando ''xxd''. 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 point (hex) 1 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 - 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 | **Dica**: no Linux, pode-se digitar caracteres Unicode usando as seguintes teclas: ''ctrl'' + ''shift'' + ''u'', código hexadecimal, ''enter'' ===== 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: $ file exemplo.* exemplo.c: C source, ISO-8859 text exemplo.html: HTML document, ASCII text exemplo.txt: UTF-8 Unicode text A conversão de codificação de um arquivo de texto pode ser feita com utilitários específicos, como o ''iconv'' no Linux: iconv -f ISO-8859-15 -t UTF-8 < input.txt > output.txt Além disso, os editores de texto geralmente permitem escolher a codificação ao salvar o arquivo. Por exemplo, no VI: :set fileencoding=utf8 :w myfilename ===== 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