INF1018 - Software Básico - 2013.1
Primeiro Trabalho
Transformação entre as representações UTF-8 e UTF-16
O objetivo deste trabalho é implementar, na linguagem C, duas funções
(utf16to8
e utf8to16
), que realizam
a transformação entre as representações UNICODE UTF-16 e UTF-8.
Leia com atenção o enunciado do trabalho e as instruções para a entrega.
Em caso de dúvidas, não invente. Pergunte!
Codificação UTF
Caracteres de um texto são em geral representados em C pelo tipo char, que
tipicamente tem o tamanho de 1 byte e pode armazenar apenas
256 (28) valores diferentes.
Enquanto a codificação ASCII utiliza apenas inteiros de 0 a 127, uma outra
codificação, chamada ISO Latin1, usa a faixa de valores 128-255 para
caracteres acentuados e alguns símbolos.
Porém isto não é suficiente para armazenar caracteres dos alfabetos
utilizados pelos diversos idiomas falados atualmente (inglês, português,
chinês, árabe, hebraico, etc.).
Para lidar com essa questão foi criada a codificação UNICODE,
que é capaz de representar caracteres de todos os
idiomas conhecidos, além de símbolos e outros tipos de caracteres.
Cada caracter em UNICODE tem um código na faixa de 0 a 0x10FFFF,
o que permite a utilização de 1114112 valores diferentes como códigos
de caracteres. Na notação adotada pelo padrão UNICODE,
U+xxxx identifica o código com valor hexadecimal xxxx.
Por exemplo, o código U+00A9 (o código do símbolo ©) corresponde ao
valor hexadecimal 0x00A9.
Existem algumas formas diferentes de codificação de caracteres UNICODE.
Para este trabalho, as codificações de interesse são a UTF-16
e a UTF-8.
Formato UTF-8
No formato UTF-8, os caracteres tem códigos de tamanho variável, podendo
ocupar de 1 a 4 bytes.
O código de um caracter tem tamanho mínimo de um byte (8 bits); se seu código
necessitar de mais espaço para ser representado, o formato UTF-8 utiliza mais
bytes.
Uma característica importante é que a codificação UTF-8 é compatível com o
padrão ASCII, ou seja, os 128 caracteres associados aos códigos de 0 a 0x7F
em ASCII tem a mesma representação em UTF-8.
A tabela a seguir indica para cada faixa de valores de códigos UNICODE
o número de bytes necessários para representá-los e a codificação usada
para essa faixa.
Código UNICODE |
Representação UTF-8 |
U+0000 a U+007F | 0xxxxxxx |
U+0080 a U+07FF | 110xxxxx 10xxxxxx |
U+0800 a U+FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
U+10000 a U+10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
Note que:
- x é o valor de um bit. O bit x da extrema direita é o bit menos
significativo.
- Apenas a menor representação possível de um caracter deve ser utilizada.
- Em uma sequência de vários bytes, o número de 1s no início do
primeiro byte indica o número total de bytes da sequência. Esses 1s
são seguidos sempre por um 0.
- Todos os bytes que se seguem ao primeiro começam com 10 (indicando que é um byte de
continuação).
Exemplos:
- O símbolo © tem código UNICODE U+00A9.
Em binário A9 é 1010 1001.
Usando a codificação de 2 bytes para a faixa U+0080 a U+07FF temos:
11000010
10101001 = 0xC2 0xA9
- O símbolo ≠ tem código UNICODE U+2260.
Em binário 2260 é 0010 0010 0110 0000.
Usando a codificação de 3 bytes para a faixa U+0800 a U+FFFF temos:
11100010
10001001
10100000 = 0xE2 0x89 0xA0
Formato UTF-16
No formato UTF-16, os caracteres são codificados em uma ou duas
"palavras" (code units) de 16 bits.
Códigos UNICODE na faixa U+0000 a U+FFFF são representados
em UTF-16 pelo seu próprio valor numérico (em 16 bits).
Exemplos:
- O caractere 'z minúsculo' tem código UNICODE
U+007A
e é representado em UTF-16 por
0x007A.
- O caractere grego β (beta minúsculo) tem código UNICODE
U+03B2
e é representado em UTF-16 por
0x03B2.
- O símbolo Han 水 (água) tem código UNICODE
U+6C34
e é representado em UTF-16 por
0x6C34.
Códigos UNICODE na faixa U+10000 a U+10FFFF são codificados em
um par de code units de 16 bits,
cada um deles com um valor no intervalo [0xD800, 0xDFFF].
(Como os valores numéricos nesse intervalo
não são usados para representação de caracteres em UNICODE, não há
risco de confundí-los com códigos UTF-16
para caracteres UNICODE na faixa U+0000 a U+FFFF).
Para encontrar o par de code units para um caractere
UNICODE na faixa U+10000 a U+10FFFF é executado o seguinte
algoritmo:
- Subtrai-se 0x10000 do valor numérico do código UNICODE.
O resultado é um valor de 20 bits no intervalo [0, 0xFFFFF].
- Os 10 bits mais significativos (que formam um valor no intervalo
[0, 0x03FF]) são somados com 0xD800. O resultado é o
primeiro code unit (um valor no intervalo [0xD800, 0xDBFF]).
- Os 10 bits menos significativos (também no intervalo
[0, 0x03FF]) são somados com 0xDC00. O resultado é o
segundo code unit (um valor no intervalo [0xDC00, 0xDFFF]).
Exemplo:
- O símbolo 𝄞 (clave de sol) tem código UNICODE
U+1D11E.
Subtraindo 0x10000 obtemos 0xD11E, em binário
0000110100 0100011110.
Os 10 bits mais significativos (0000110100 = 0x0034), somados a 0xD800
fornecem o primeiro code unit:
0xD834.
Os 10 bits menos significativos (0100011110 = 0x011E), somados a 0xDC00
fornecem o segundo code unit:
0xDD1E.
Funções de transformação
Conversão UTF-8 para UTF-16:
A função utf8to16
deve ler o conteúdo de um arquivo de
entrada (um texto codificado em UTF-8) e gravar em um arquivo de saída esse
mesmo texto, codificado em UTF-16.
No arquivo com formato UTF-16, os dois primeiros bytes do
arquivo armazenam o BOM (Byte Order Mark, código 0xFEFF), que indica
se os valores dos code units a seguir estão little-endian ou
big-endian.
A sequência 0xFE 0xFF indica que os code units
estão em big-endian.
A sequência 0xFF 0xFE indica que os code units
estão em little-endian.
O protótipo (cabeçalho) da função utf8to16
é o
seguinte:
int utf8to16(FILE *arq_entrada, FILE *arq_saida, int ordem);
Os dois primeiros parâmetros da função são dois arquivos binários abertos:
o arquivo de entrada (arq_entrada
) e o arquivo de
saída (arq_saida
).
O terceiro parâmetro especifica a ordenação do arquivo de saída:
- 0 indica ordenação little-endian
- 1 indica ordenação big-endian
O valor de retorno da função utf8to16
é 0, em caso de
sucesso e -1, em caso de erro. Dois tipos de erros podem ocorrer:
- erro de E/S: neste caso, a função deve emitir, na saída de erro (stderr),
uma mensagem indicando o tipo de erro ocorrido (leitura ou gravação)
- codificação incorreta no arquivo de entrada: neste caso, a função deve
emitir, na saída de erro (stderr) uma mensagem indicando a posição no
arquivo de entrada onde o erro foi detetado (o índice do primeiro byte
inconsistente, considerando-se que o primeiro byte do arquivo é o byte 0).
Nos dois casos, após emitir a mensagem de erro, a função deve retornar.
Conversão UTF-16 para UTF-8:
A função utf16to8
deve ler o conteúdo de um arquivo de
entrada (um texto codificado em UTF-16) e gravar em um arquivo de saída esse
mesmo texto, codificado em UTF-8. O protótipo da função é o seguinte:
int utf16to8(FILE *arq_entrada, FILE *arq_saida);
Os parâmetros da função são dois arquivos binários abertos:
o arquivo de entrada (arq_entrada
) e o arquivo de
saída (arq_saida
).
Note que, neste caso, a própria função descobre a ordenação do arquivo de
entrada (UTF-16), inspecionando os primeiros dois bytes do seu conteúdo.
Assim como na função anterior, o valor de retorno é 0, em caso de
sucesso e -1, em caso de erro. Da mesma forma, os procedimentos para os
casos de erro são:
- erro de E/S: a função deve emitir, na saída de erro (stderr),
uma mensagem indicando o tipo de erro ocorrido (leitura ou gravação)
- codificação incorreta no arquivo de entrada: a função deve
emitir, na saída de erro (stderr) uma mensagem indicando a posição no
arquivo de entrada onde o erro foi detetado (o índice do primeiro byte
inconsistente, considerando-se que o primeiro byte do arquivo é o byte 0).
Implementação e Execução
Você deve criar um arquivo fonte chamado
utfconv.c contendo as
duas funções descritas acima, e funções auxiliares, se for o caso.
Esse arquivo não deve conter uma função
main
!
Para testar seu programa, crie um outro arquivo, teste.c
,
contendo a função main
.
Crie seu programa executável teste
com a linha:
gcc -Wall -o teste utfconv.c teste.c
Tanto o arquivo utfconv.c
como teste.c
devem conter a linha:
#include "utfconv.h"
O arquivo utfconv.h deve conter apenas os protótipos (cabeçalhos)
das funções utf16to8
e utf8to16
.
Exemplos para teste
Para testar suas funções você poderá usar os arquivos:
que contém o mesmo "texto", codificado em UTF-8 e UTF-16 (little-endian e big-endian):
- Arquivos pequenos sem erro: os três arquivos contém o mesmo "texto", codificado em UTF-8 e UTF-16
(little-endian e big-endian):
- Arquivos sem erro: os três arquivos contém o mesmo "texto", codificado em UTF-8 e UTF-16
(little-endian e big-endian):
- Arquivos com erro:
Entrega
Os trabalhos deverão ser enviados por email para o professor da turma,
com subject/assunto "Trabalho 1 - Software Basico".
Indique claramente os integrantes do grupo no corpo do e-mail e anexe
os seguintes arquivos:
- o arquivo utfconv.c
- um arquivo texto contendo um pequeno relatório explicando o que está
funcionando e também (muito importante!) o que não está funcionando.
Se seu programa tem bugs, você saber disso é um atenuante...
Coloque no início do arquivo fonte, como comentário, os nomes dos
integrantes do grupo, na seguinte forma:
/* Nome_do_Aluno1 Matricula1 Turma*/
/* Nome_do_Aluno2 Matricula2 Turma*/
Coloque também no relatório os nomes dos integrantes do grupo.
Prazo
O trabalho deve ser enviado por email ao professor da turma até a meia-noite do dia 26 de abril.
Observações:
- Os trabalhos podem ser feitos em grupos de dois alunos.
- Os grupos serão chamados para apresentações orais/demontrações dos trabalhos entregues.