Em computação, caracteres de texto são tipicamente representados por códigos especificados por algum padrão de codificação. Um padrão bastante conhecido é a codificação ASCII, que utiliza valores inteiros de 0 a 127 para representar letras, dígitos e alguns outros simbolos. Algumas extensões dessa codificação utilizam também a faixa de valores de 128 a 255 para representar, por exemplo, caracteres acentuados e alguns outros símbolos adicionais.
A codificação ASCII e outros padrões de codificação que utilizam códigos de um único byte são limitados à representação de apenas 256 símbolos diferentes. Para permitir a representação de um conjunto maior de caracteres foi criada, no final da década de 1980, a codificação UNICODE. A versão corrente dessa codificação é capaz de representar os caracteres utilizados por todos os idiomas conhecidos, além de diversos outros símbolos.
Cada caractere em UNICODE é associado a um código na faixa de 0 a 0x10FFFF, o que permite a representação de 1.114.112 símbolos diferentes. 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-8 e a UTF-32.
A codificação UTF-32 é simplesmente a representação numérica do código do caracter, em um inteiro de 4 bytes. Essa forma de codificação é suficiente para representar todos os códigos previstos no UNICODE, não sendo necessária maior elaboração.
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 um único byte) 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 (byte a byte) |
|---|---|
| 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:
11000010 10101001 = 0xC2 0xA9
Note o preenchimento com zeros à esquerda para completar a porção do código do caractere colocada no primeiro byte da sequência.
11100010 10001001 10100000 = 0xE2 0x89 0xA0
O BOM funciona como uma assinatura do arquivo: além de identificar o conteúdo do arquivo como UNICODE ele também define a ordem de armazenamento do arquivo (big-endian ou little-endian). Como a ordem de armazenamento não faz sentido para arquivos UTF-8, o BOM é raramente usado no início de arquivos UTF-8, e muitas aplicações que trabalham com UTF-8 não dão suporte a um BOM no inicio de um arquivo de entrada.
A sequência exata de bytes que corresponde ao caractere BOM inserido no início do arquivo depende da codificação empregada e da ordem de armazenamento do arquivo, como mostrado a seguir:
| Tipo do Arquivo | Bytes |
|---|---|
| UTF-32, big-endian | 00 00 FE FF |
| UTF-32, little-endian | FF FE 00 00 |
O objetivo do trabalho é implementar, na linguagem C, duas funções
(conv8_32 e conv32_8), que recebem
como entrada um arquivo contendo um texto codificado em um dos dois formatos
(UTF-8 ou UTF-32) e geram como saida um arquivo contendo o mesmo texto,
codificado no outro formato.
conv8_32 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-32.
O arquivo de entrada UTF-8 não terá um caractere BOM inicial. Já o arquivo de saída deverá necessariamente conter o BOM inicial.
O protótipo (cabeçalho) da função conv8_32 é o
seguinte:
int conv8_32(FILE *arq_entrada, FILE *arq_saida, char ordem);
Os dois primeiros parâmetros da função são dois arquivos binários já 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:
conv8_32 é 0, em caso de
sucesso e -1, em caso de erro. Dois tipos de erros podem ocorrer:
conv32_8 deve ler o conteúdo de um arquivo de
entrada (um texto codificado em UTF-32) e gravar em um arquivo de saída esse
mesmo texto, codificado em UTF-8. O protótipo da função é o seguinte:
int conv32_8(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).
O arquivo de entrada deverá necessariamente conter o BOM inicial. O arquivo de saída UTF-8 não deverá conter um caractere BOM inicial.
Note que, neste caso, a própria função descobre a ordenação do arquivo de entrada (UTF-32), inspecionando o BOM no início do arquivo.
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:
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!
Crie também um arquivo
utfconv.h ,
que deve conter apenas
os protótipos das funções conv32_8 e conv8_32.
Mantenha os protótipos especificados no enunciado do trabalho,
sem quaisquer alterações!
Para testar seu programa, crie um outro arquivo, por exemplo,
teste.c,
contendo uma função main.
Crie seu programa executável, por exemplo teste, com a linha:
gcc -Wall -o teste utfconv.c teste.c
Tanto o arquivo utfconv.c como
teste.c deve conter a linha:
#include "utfconv.h"
Para testar suas funções você poderá usar os arquivos a seguir:
Devem ser entregues via Moodle dois arquivos:
/* Nome_do_Aluno1 Matricula Turma */ /* Nome_do_Aluno2 Matricula Turma */
Coloque também na área de texto da tarefa o nome dos integrantes do grupo
Apenas uma entrega é necessária (usando o login de um dos integrantes do grupo)