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+007F0xxxxxxx
U+0080 a U+07FF110xxxxx 10xxxxxx
U+0800 a U+FFFF1110xxxx 10xxxxxx 10xxxxxx
U+10000 a U+10FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Note que:

Exemplos:

  1. 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

  2. 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:

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:

  1. Subtrai-se 0x10000 do valor numérico do código UNICODE. O resultado é um valor de 20 bits no intervalo [0, 0xFFFFF].
  2. 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]).
  3. 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:




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: 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: 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:



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):

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:

  1. o arquivo utfconv.c
  2. 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: