ferramenta de compressÃo de arquivos open source Área de ...siaibib01.univali.br/pdf/deivid paulo...
TRANSCRIPT
UNIVERSIDADE DO VALE DO ITAJAÍ CENTRO DE CIÊNCIAS TECNOLÓGICAS DA TERRA E DO MAR
CURSO DE CIÊNCIA DA COMPUTAÇÃO
FERRAMENTA DE COMPRESSÃO DE ARQUIVOS OPEN SOURCE
Área de Algoritmos
por
Deivid Paulo Schmidt
Rudimar Luís Scaranto Dazzi, M.Sc. Orientador
Itajaí (SC), junho de 2007.
2
UNIVERSIDADE DO VALE DO ITAJAÍ CENTRO DE CIÊNCIAS TECNOLÓGICAS DA TERRA E DO MAR
CURSO DE CIÊNCIA DA COMPUTAÇÃO
FERRAMENTA DE COMPRESSÃO DE ARQUIVOS OPEN SOURCE
Área de Estrutura de Algoritmos
por
Deivid Paulo Schmidt Relatório apresentado à Banca Examinadora do Trabalho de Conclusão do Curso de Ciência da Computação para análise e aprovação. Orientador: Rudimar Luís Scaranto Dazzi, M.Sc.
Itajaí (SC), junho de 2007.
ii
SUMÁRIO
LISTA DE ABREVIATURAS..................................................................iv
LISTA DE FIGURAS.................................................................................v
LISTA DE TABELAS ...............................................................................vi LISTA DE EQUAÇÕES ......................................................................... vii RESUMO ................................................................................................. viii ABSTRACT................................................................................................ix
1 INTRODUÇÃO ......................................................................................1 1.1 PROBLEMATIZAÇÃO...................................................................................3 1.1.1 Formulação do Problema...............................................................................3 1.1.2 Solução Proposta ............................................................................................3 1.2 OBJETIVOS .....................................................................................................3 1.2.1 Objetivo Geral ................................................................................................3 1.2.2 Objetivos Específicos......................................................................................3 1.3 METODOLOGIA.............................................................................................4 1.4 ESTRUTURA DO TRABALHO......................................................................4
2 FUNDAMENTAÇÃO TEÓRICA ........................................................6 2.1 COMPRESSÃO DE DADOS ...........................................................................6 2.1.1 História ...........................................................................................................6 2.1.2 Compactação de Dados e Compressão de Dados..........................................7 2.2 TIPOS DE COMPRESSÃO .............................................................................7 2.2.1 Compressão com Perdas ................................................................................8 2.2.2 Compressão sem Perda ..................................................................................8 2.3 ALGORITMOS DE COMPRESSÃO CLÁSSICOS.....................................10 2.3.1 Shannon-Fano...............................................................................................10 2.3.2 Huffman........................................................................................................12 2.3.3 Ziv e Lempel .................................................................................................15 2.3.4 Comparação dos Algoritmos........................................................................19 2.4 ALGORITMOS ADAPTADOS .....................................................................20 2.4.1 LZSS..............................................................................................................20 2.4.2 LZW..............................................................................................................24 2.4.3 Burrows-Wheeler .........................................................................................26 2.5 SOFTWARE LIVRE......................................................................................31 2.6 PROPRIEDADE INTELECTUAL................................................................31 2.7 LICENÇAS DE SOFTWARE........................................................................32 2.8 FERRAMENTAS SIMILARES.....................................................................33
3 PROJETO .............................................................................................39
3.1 FERRAMENTA DE COMPRESSÃO DE DADOS OPEN SOURCE.........39
iii
3.1.1 Implementação do Método LZW.................................................................40 3.1.2 Implementação do Método BW + RLE.......................................................43
4 TECNOLOGIAS UTILIZADAS ........................................................46
4.1.1 Borland Delphi .............................................................................................46 4.1.2 C++................................................................................................................47 4.1.3 PHP ...............................................................................................................48
5 DESENVOLVIMENTO ......................................................................48 5.1 MODELAGEM DO SISTEMA......................................................................48 5.1.1 DIAGRAMAS DE CASOS DE USO (USE CASE) .....................................48 5.1.2 DIAGRAMA DE SEQUÊNCIA ..................................................................51 5.2 TELAS DO SISTEMA....................................................................................52 5.3 MÉTODOS......................................................................................................55 5.4 COMUNICAÇÃO ENTRE APLICATIVOS ................................................56 5.5 TESTES E RESULTADOS............................................................................56
6 CONCLUSÕES ....................................................................................57
REFERÊNCIAS BIBLIOGRÁFICAS ...................................................58
iv
LISTA DE ABREVIATURAS
ASCII American Standart Code for Information Internchange BW Burrows-Wheeler HUFF Huffman LZ Lempel-Ziv LZSS Lempel-Ziv-Storer-Szymanski LZW Lempel-Ziv-Welch PPMD Prediction by Partial Match RLE Run Length Encoding SF Shannon Fano TCC Trabalho de Conclusão de Curso UNIVALI Universidade do Vale do Itajaí PHP Hypertext Preprocessor
v
LISTA DE FIGURAS
Figura 1. Codificação pelo código de Huffman. ............................................................................ 10 Figura 2. Exemplo do código de Shannon-Fano. ........................................................................... 11 Figura 3. Exemplo da divisão de Shannon-Fano............................................................................ 12 Figura 4. Árvore binária resultante de Shannon-Fano.................................................................... 12 Figura 5. Exemplo de árvore do código de Huffman. .................................................................... 14 Figura 6. Exemplo da árvore binária de Huffman. ......................................................................... 14 Figura 7 Exemplo de janela deslizante do algoritmo LZ77. ........................................................... 16 Figura 8. Exemplo da decodificação do LZ77. .............................................................................. 17 Figura 9. Codificando uma string com LZ78................................................................................. 18 Figura 10. Decodificando uma string com LZ78. .......................................................................... 19 Figura 11. Exemplo de arvore binária de LZSS e uma árvore quase balanceada. ........................... 21 Figura 12. Buffer de busca do LZSS.............................................................................................. 22 Figura 13. O coração do código do LZW. Codificador e decodificador são mostrados .................. 26 Figura 14. Exemplo de codificação usando Burrows-Wheeler....................................................... 28 Figura 15.Codificando uma string com BW. ................................................................................. 30 Figura 16. Matriz resultante do processo de codificação pelo método BW. ................................... 31 Figura 17. Tela principal do WinRar. ............................................................................................ 34 Figura 18. Tela principal do WinZip. ............................................................................................ 34 Figura 19. Tela principal do 7Zip. ................................................................................................. 35 Figura 20. Codificando uma stringusando o método LZW. ........................................................... 41 Figura 22. Caso de Uso do Sistema. .............................................................................................. 49 Figura 23. Cenário para o Caso de Uso para criar um arquivo codificado. ..................................... 50 Figura 24. Cenário para o Caso de Uso para decodificar um arquivo codificado............................ 51 Figura 21. Diagrama de seqüência do sistema. .............................................................................. 52 Figura 25. Tela principal da Interface............................................................................................ 53 Figura 26. Tela de compressão da Interface................................................................................... 53 Figura 27. Tela final da codificação. ............................................................................................. 54 Figura 28. Tela de descompressão da Interface. ............................................................................ 55
vi
LISTA DE TABELAS
Tabela 1. Comparação dos Algoritmos.......................................................................................... 19 Tabela 2. Ferramentas Similares ................................................................................................... 37 Tabela 3. Ferramentas similares codificando diversos arquivos. .................................................... 38 Tabela 4. Descrição das etapas de criação de um arquivo compactado no WinZip......................... 40 Tabela 5. Função Principal do Método LZW................................................................................. 42 Tabela 6. Função Para Decodificar em LZW................................................................................. 43 Tabela 7. Função Para Codificar em LZW. ................................................................................... 43 Tabela 8. Função Principal do Método Híbrido BW + LRE............................................................ 45 Tabela 9. Função que Codifica um Bloco de Dados Usando BW................................................... 45 Tabela 10. Função que Codifica um Bloco de Dados Usando BW................................................. 46 Tabela 11. Função que Codifica uma String de Caracteres Usando RLE. ...................................... 46 Tabela 12. Função que Decodifica uma String de Caracteres Usando RLE.................................... 46 Tabela 13. Comparação entre ferramentas..................................................................................... 56
vii
LISTA DE EQUAÇÕES
Equação 1. Cálculo da entropia. .................................................................................................... 11
viii
RESUMO
Schmidt, Deivid Paulo. FERRAMENTA DE COMPRESSÃO DE ARQUIVOS OPEN SOURCE. Itajaí, 2007. 69 f. Trabalho de Conclusão de Curso (Graduação em Ciência da Computação)–Centro de Ciências Tecnológicas da Terra e do Mar, Universidade do Vale do Itajaí, Itajaí, 2006. Nos dias de hoje, com a grande difusão da informática e da Internet em nosso meio, a comunicação tornou-se muito intensa entre pessoas e organizações em todos os lugares do mundo, seja enviando e-mails ou transferindo arquivos. Porém, a cada dia que passa, o volume de informação aumenta e os arquivos estão ficando maiores, ocupando muito espaço quando armazenados em meio físico, e lentos para transferir via Internet. Qualquer byte poupado no armazenamento da informação ou na transferência de um computador para outro, traz redução de custo e de tempo ao usuário. O sistema proposto nesse trabalho busca agilizar a transferência e minimizar o tamanho ocupado por esses arquivos, facilitando essas tarefas para o usuário. O sistema tem como proposta ser um núcleo onde os dados serão codificados e decodificados, através de uma interface para Microsoft Windows. As tecnologias que foram utilizadas são o Borland Delphi e C ANSI para a programação. Os dados para análise são provenientes de testes realizados com ferramentas similares existentes WinZip, WinRar e 7Zip. O sistema oferece as principais funcionalidades das ferramentas disponíveis no mercado. O sistema permitirá a criação e edição de arquivos codificados pela ferramenta. O sistema é voltado para todos os usuários de computador que necessitem de uma ferramenta de compressão de dados uma vez que os arquivos ficam com tamanho sensivelmente menor. Palavras-chave: Compressão de dados. Armazenamento. Open-Source.
ix
ABSTRACT
Nowadays, with the great diffusion of computer science and the Internet in our way, the
communication very became intense between people and organizations in all the places of the
world, either sending e-mails or transferring archives. However, to each day that passes, the
volume of information increases and the archives are being bigger, occupying much space when
stored in environment, and slow to transfer way Internet. Any byte saved in the storage of the
information or the transference of a computer for another one, brings time and cost reduction to the
user. The system considered in this work searchs to speed the transference and to minimize the busy
size for these archives, being facilitated these tasks for the user. The system has as proposal to be a
nucleus where the data will be codified and decoded, through an interface for Microsoft Windows.
The technologies that had been used are the Borland Delphi and C ANSI for the programming. The
data for analysis are proceeding from tests carried through with existing similar tools WinZip,
WinRar and 7Zip. The system offers the main functionalities of the available tools in the. The
system will allow to the creation and edition of archives codified for the tool. The system is come
back toward all the users of computer who need a tool of compression of data a time that the
archives are with lesser size significantly.
Keywords: Compression of data. Storage. Open- source.
1 INTRODUÇÃO
A evolução da tecnologia na área da informática proporcionou uma redução nos custos dos
componentes de computadores e, conseqüentemente, um número maior de pessoas pode utilizar-se
dessa tecnologia. Os avanços permitiram que mais processamentos pudessem ser realizados em
menos tempo, e aumentou a quantidade de informação armazenada disponível ao usuário de
computador.
Esse aumento de processamento acarretou em arquivos binários de maior tamanho, arquivos
do tipo texto, imagem, som e vídeo começaram a ficar com volumes excessivos, difícieis de serem
armazenados, compartilhados ou distribuídos. Uma vez que praticamente todos os computadores
estão conectados a Internet ou a algum outro tipo de rede, a transferência desses arquivos tornou-se
uma prática comum e em grande escala, o volume gerado pela transferência desses arquivos é um
problema, já que este pode ocupar quase toda a banda de comunicação.
Adam Drozdek (2002) comenta que, em qualquer tipo de rede de computadores, a troca de
informação se faz de forma essencial para que seja apropriado o funcionamento de qualquer nível
em qualquer tipo de organização que trabalha com o uso de computadores. Quanto mais rápido essa
transferência de informação ocorra, melhor é a eficiência do funcionamento dessa organização. O
uso da compressão dos dados está diretamente relacionado ao menor espaço ocupado pelo arquivo
codificado do que o arquivo original ocupa e ao tempo de transferência de um computador a outro.
Um arquivo de texto com 2,988,578 bytes codificado pela ferramenta WinZip 9.0, uma das
ferramentas de compressão de dados mais comuns do mercado fica com apenas 801,593 bytes,
ocupando apenas 26.82% do espaço ocupado originalmente.
Pela grande utilização de ferramentas de compressão de dados e pelos ótimos resultados que
essas ferramentas obtêm em ordem de redução de espaço e de tempo de transferência de arquivos,
este projeto busca desenvolver uma ferramenta de compressão de dados com soluções híbridas, o
que estas metas serão alcançadas após um estudo das técnicas de compressão existentes, Open-
Source e de distribuição livre sem custo algum ao usuário final desta ferramenta.
A ferramenta é dividida em duas partes, o núcleo principal e a interface do sistema. Ambos
serão executáveis independentes, sendo o núcleo responsável por toda a manipulação dos arquivos e
2
a interface será responsável pela interação do usuário com o núcleo, passando as configurações
através de troca de mensagens para o núcleo.
A compressão dos arquivos será realizada no núcleo da ferramenta, avaliando o conteúdo do
arquivo e utilizando o melhor método de compressão.
Essa ferramenta será especificamente voltada para compressão de arquivos para ganho de
espaço de armazenamento de dados. Objetiva-se alcançar a mesma eficiência e funcionalidades de
ferramentas similares disponíveis hoje no mercado, como por exemplo, às ferramentas WinZip 9.0 e
o WinRar 3.5.
Algumas das funcionalidades desejadas para a ferramenta:
• Compactar mais de um arquivo;
• Integração com o Microsoft Windows; e
• Adicionar ou remover arquivos do arquivo compactado.
Um projeto similar chamado 7-zip (2005) propõe-se em desenvolver uma ferramenta de
compressão de dados Freeware para utilização em massa. Esse projeto já obteve aproximadamente
86,119 downloads realizados até o momento em que foi realizada essa pesquisa, mostrando a
grande aceitação por uma ferramenta dessa categoria. Maiores informações a respeito desse projeto
podem ser encontradas através do endereço eletrônico (http://www.7-zip.org).
A realização deste trabalho também se justifica em nível de Trabalho de Conclusão de Curso
para o Curso de Ciência da Computação, pois trata do desenvolvimento de um projeto
computacional que faz uso de varias técnicas da área, além de ser um trabalho precursor no curso.
Esse também será um trabalho que contribuirá para a disciplina de Estruturas de Dados, gera
material de pesquisa e apresenta soluções em compressão de arquivos, tópico da disciplina.
3
1.1 PROBLEMATIZAÇÃO
1.1.1 Formulação do Problema
Uma dificuldade na área de informática é o armazenamento de arquivos que ocupam muito
espaço em meios de armazenamentos e como transmitir esses dados com melhor performance pela
rede ou Internet.
1.1.2 Solução Proposta
A solução proposta para este projeto final de curso, é o desenvolvimento de um núcleo
desenvolvido em C ANSI, com uma interface desenvolvida na ferramenta Borland Delphi, para o
sistema operacional Windows. Esta ferramenta será capaz de realizar a compressão e
descompressão de dados para que os mesmos ocupem menor espaço em meios de armazenamentos
e possam ser compartilhadas com maior velocidades.
1.2 OBJETIVOS
1.2.1 Objetivo Geral
Este projeto tem por objetivo construir um compressor de arquivos de uso geral que será de
código aberto e sem custo.
1.2.2 Objetivos Específicos
• Definir quais as técnicas de compressão serão utilizadas no sistema;
• Modelar o sistema proposto;
• Implementar o sistema de compressão;
• Testar e validar o sistema;
• Documentar o sistema.
• Disponibilizar para estudos e futuras pesquisas os códigos e documentação gerados
4
1.3 METODOLOGIA
Foram estipuladas seis etapas a fim de executar este projeto. As três primeiras etapas foram efetivadas no TCC I e compreenderam a análise das tecnologias envolvidas e o projeto do sistema. As etapas restantes foram efetivadas no TCC II e compreendem a modelagem, desenvolvimento, validação e documentação do sistema.
Para a análise das tecnologias envolvidas, foi feito um estudo nas técnicas clássicas de compressão e pesquisa em ferramentas similares disponíveis no mercado.
No projeto do sistema foram definidas quais as técnicas que foram usadas no desenvolvimento do sistema, foi feita a organização do material estudado e realizada a análise e projeto do sistema.
A modelagem e o desenvolvimento do sistema foram realizados a partir das soluções encontradas nas pesquisas e estudos dos métodos de compressão de dados.
A validação foi efetuada observando os resultados dos testes realizados com a ferramenta desenvolvida e comparando-os com os resultados de ferramentas disponíveis.
Na documentação do sistema foi descrita a solução encontrada e como ela foi implementada no sistema.
1.4 ESTRUTURA DO TRABALHO
O trabalho está dividido em quatro capítulos: Introdução, Fundamentação Teórica, Projeto e Conclusões.
No Capítulo Fundamentação Teórica é exposto o conteúdo teórico do trabalho fundamentado nas bibliografias indicadas no próprio texto. Este capítulo está dividido em 10 sessões:
• Compressão de Dados;
• Tipos de Compressão;
• O que é um Algoritmo;
• Algoritmo de Compressão de Dados;
• Algoritmos de Compressão Clássicos;
• Algoritmos Adaptados;
• Software Livre;
• Propriedade Intelectual;
• Licenças de Software; e
5
• Ferramentas Similares.
No Capítulo Projeto é apresentado à proposta de desenvolvimento do sistema, assim como os diagramas de Use Case e de Seqüência.
No Capítulo Conclusões são expostas algumas considerações gerais sobre o trabalho desenvolvido.
2 FUNDAMENTAÇÃO TEÓRICA
2.1 Compressão de Dados
A compressão de dados nunca foi tão amplamente usada como nos dias atuais. A cada dia
deseja-se mais “espremer” as informações de forma que mais dados possam ser transmitidos em
menor tempo, ou armazenados ocupando menos espaço nos meios de armazenamentos. Segundo
Pinheiro (2006), a compressão de dados envolve uma redução intencional ou inevitável no conteúdo
da informação dos dados a serem transmitidos ou armazenados.
2.1.1 História
O código Morse inventado em 1838 para uso em telégrafos é, segundo Wolfram (2002), um
dos primeiros exemplos de compressão de dados baseados em pequenos dicionários de palavras. A
era moderna da compressão de dados começou no final de 1940 com o desenvolvimento da teoria
da informação.
Em 1949, Claude Shannon e Robert Fano planejaram um método sistemático para atribuir
dicionários de palavras baseados em probabilidades por blocos. Um método ótimo para fazer isso
foi descoberto por David Huffman em 1951. As primeiras implementações foram feitas
principalmente em hardware, com escolhas especificas de dicionários de palavras para fazer um
acordo entre compressão e a correção de erro.
Na metade dos anos 70, surgiu a idéia de atualizar dinamicamente os dicionários de palavras
do método de Huffman, baseados em dados reais encontrados. No final dos anos 70, começando a
se tornar comum o armazenamento on-line de textos, programas de compressão de dados
começaram a ser desenvolvidos, quase todos baseados no código adaptativo de Huffman.
Em 1977, Abraham Lempel e Jacob Ziv, sugeriram a idéia de dicionário de palavras
adaptativo. Na metade dos anos 80, acompanhando o trabalho de Terry Welch, o tão falado
algoritmo de LZW, rapidamente tornou-se o método escolhido para o uso geral em sistemas de
compressão. Esse método foi utilizado em programas como o PKZIP e também em hardwar,e como
nos modems.
No final dos anos 80, imagens digitais começaram a ficar comuns, e os padrões para
comprimí-las surgiram. No começo dos anos 90, métodos de compressão com perda de informação
7
também começaram a ser amplamente usados. Alguns padrões de compressão de imagens
atualmente incluem: FAX CCITT 3 (Huffman); GIF (LZW); JPEG (lossy - Huffman); BMP; TIFF.
Atualmente as relações de compressão para texto estão ao redor de 3:41, e para diagramas de linhas
e imagens de texto estão em 3:1, e para imagens fotográficas estão em torno de 2:1 para compressão
sem perdas e 20:1 para compressão com perdas.
2.1.2 Compactação de Dados e Compressão de Dados
Segundo Souza (2002), apesar de ambas as técnicas buscarem o mesmo objetivo que é
diminuir o volume dos dados a serem armazenados ou transmitidos, existe uma diferença entre
compactação de dados e compressão de dados.
2.1.2.1 Compactação de Dados
A compactação de dados faz uma nova representação dos dados, utilizando um novo
alfabeto, ou seja, utiliza um novo conjunto de representação, menor que o utilizado no original.
Um exemplo seria a conversão dos dados no formato EBCDIC de 8 bits para o formato
ASCII de 7 bits. Neste caso há uma redução do volume de dados, pois representamos cada caractere
com menos bits.
2.1.2.2 Compressão de Dados
A compressão não transforma os dados em um outro alfabeto de representação, mas sim
codifica a mensagem.
Um exemplo dessa codificação é termos vários caracteres repetidos numa seqüência, e
substituir essa seqüência por um código. Exemplo: AATTTT representa-se por: *4T, em que *
corresponde ao código de duas letras A e 4T ao código de quatro letras T. Esses códigos são
tabelados. A tabela também é informada ao destino que ao receber os dados codificados, decodifica-
os por meio do mesmo algoritmo de origem.
2.2 Tipos de Compressão
Segundo Souza (2002), pode-se definir dois tipos de compressão de dados:
• Com perda de informação (que pode oferecer uma melhor performance, mas com
perdas de dados devido aos mesmos serem escolhidos em amostragem); e
8
• Sem perda de informação (em que todos os dados devem chegar ao seu destino
completos).
2.2.1 Compressão com Perdas
Na compressão com perdas, como o próprio nome sugere, uma parte da informação é
perdida durante o processo de codificação. A perda nestes casos não altera significativamente os
dados originais. Um exemplo conhecido da compressão com perdas é o da transformação dos
arquivos de áudio para o padrão MP3, onde a qualidade do som continua perfeitamente audível para
os seres humanos. Normalmente o grau de compressão desta categoria é muito maior do que o
obtido na compressão sem perdas, normalmente na faixa de 50:1 e podendo chegar até 10000:1.
(SOUZA, 2002),
Além do exemplo citado acima, tem-se outras aplicações como:
• Armazenamento de sinais analógicos convertidos em digitais de aplicações médicas
como eletrocardiograma e eletroencefalograma;
• Armazenamento de imagens digitalizadas de baixa resolução; e
• Compressão de imagens de vídeo.
Como este projeto se propõe a fazer uma ferramenta que codifique um arquivo e depois o
decodifique voltando ao seu tamanho original sem perda alguma, não serão utilizadas técnicas de
compressão de dados com perdas no sistema, já que o objetivo não é transformar o arquivo e sim
codificá-lo para que ocupe menor tamanho físico e depois possa ser restaurado.
2.2.2 Compressão sem Perda
Na compressão sem perdas segundo Souza (2002), nenhum bit do arquivo original é perdido
após o processo de codificação. Assim, comprime-se um arquivo e o descomprime-se em seguida, o
arquivo original é restaurado sem nenhum tipo de perda. Ao comprimir um arquivo texto com a
ferramenta Winzip, por exemplo, estamos usando uma técnica de compressão sem perdas. Alguns
métodos:
• Método orientado a caractere (dicionário fixo): Utiliza uma tabela com as seqüências de
caracteres mais prováveis de ocorrer. Cada seqüência mais provável possui o seu código de
9
representação. A taxa de compressão é tão maior quanto os dados do arquivo a ser
codificado tenham caracteres repetidos como o previsto na montagem da tabela; e
• Método de codificação estatística (dicionário dinâmico): A tabela de conversão nesse
método é formada a partir da detecção e identificação das seqüências na própria mensagem
que está chegando para ser comprimida, altera-se e adapta-se à tabela de acordo com as
mensagens de entrada.
No método orientado a caractere a tabela de codificação não precisa ser transmitida para o
receptor junto com a mensagem, pois o receptor responsável pela decodificação (descompressão) já
possui a mesma tabela, que é fixa e produz uma baixa taxa de compressão.
Já no método de codificação estatística, as tabelas são atualizadas periodicamente, de acordo
com as mensagens que chegam, analisando a ocorrência de novas seqüências e alternando a tabela
para obter uma maior taxa de compressão. Esse método utiliza o algoritmo de Huffman para a
montagem da tabela de codificação. O algoritmo de Huffman funciona da seguinte forma. (SOUZA,
2002):
• Primeiramente, determina-se a freqüência que cada caractere ocorre na mensagem a ser
comprimida; e
• Depois, codifica-se esse caractere com a menor quantidade de bits possível. Os
caracteres seguintes serão codificados com mais bits crescentemente.
Exemplo: Vamos decodificar a palavra UNIVALI.
O algoritmo de Huffman representa com menos bits os caracteres que mais se repetem, os
caracteres que tem uma repetição menor são deixados para o final da tabela, com um numero maior
de bits.
A Figura 1 mostra a representação dos caracteres na tabela:
10
Figura 1. Codificação pelo código de Huffman.
Fonte: Autor.
A representação da palavra UNIVALI ficará: 100 101 11 010 011 00 11.
Portanto, a palavra que ocupava 7 bytes (1 byte para cada caractere) passa agora a ocupar 3 bytes no
exemplo acima. A taxa de compressão foi de 1:2,33. A palavra passou a ser representada em
42.85% do seu tamanho original.
2.3 Algoritmos de Compressão Clássicos
2.3.1 Shannon-Fano
O código de Shannon-Fano, segundo Salomon (2004), foi o primeiro método desenvolvido a
encontrar bons códigos de tamanho variável. O código começa com um jogo de n símbolos com
probabilidades conhecidas (ou freqüências) de ocorrência. Os símbolos são arranjados
primeiramente em ordem descendente das suas probabilidades. O jogo dos símbolos é dividido em
dois subconjuntos que têm as mesmas (ou quase) probabilidades. Todos os símbolos em um
subconjunto contêm códigos que começam com 0, enquanto os códigos dos símbolos no outro
subconjunto começam com 1. Cada subconjunto é dividido então recursivamente em dois, e o
segundo bit de todos os códigos é determinado em uma maneira similar. Quando um subconjunto
contém apenas dois símbolos, seus códigos são diferenciados adicionando mais um bit a cada um. O
processo continua até que nenhum subconjunto permaneça. A Figura 2 ilustra o código de Shannon-
Fano para um alfabeto de sete símbolos. Observe que os símbolos não são mostrados, somente suas
probabilidades.
11
Figura 2. Exemplo do código de Shannon-Fano.
Fonte: Salomon (2004).
A primeira etapa separa o jogo de sete símbolos em dois subconjuntos, primeiro com dois
símbolos e uma probabilidade total de 0.45, segundo com os cinco símbolos restantes e uma
probabilidade total de 0.55. O é atribuído 1 ao início do código dos primeiros dois símbolos no
primeiro, então seus códigos finais são 11 e 10. O segundo subconjunto é dividido, na segunda
etapa, em dois símbolos (com probabilidade total 0.3 e códigos que começam com 01) e em três
símbolos (com probabilidade total 0.25 e códigos que começam com 00). A etapa três divide os
últimos três símbolos em 1 (com probabilidade 0.1 e código 001) e 2 (com probabilidade total 0.15
e códigos que começam com 000).
O tamanho médio deste código é 0.25 x 2 + 0.20 x 2 + 0.15 x 3 + 0.15 x 3 + 0.10 x 3 + 0.10
x 4 + 0.05 x 4 = 2.7 bits/símbolos. Este é um resultado bom porque a entropia (o menor número de
bits necessários, na média, para representar cada símbolo) é:
67,2)05,0log05,010,0log10,010,0log10,0
15,0log15,015,0log15,020,0log20,025,0log25,0(
222
2222
≈+++
+++−
Equação 1. Cálculo da entropia.
Um outro exemplo do algoritmo de Shannon-Fano é uma string de letras (A - E) e tomando
suas freqüências, A = 14, B = 7, C = 5, D = 5 e E = 5. A primeira linha divisora é colocada entre o
“B” e o “C”, atribuindo uma contagem de 21 ao grupo superior e de 14 ao grupo inferior, que é o
mais próximo à metade. Isto significa que “A” e “B” terão o código binário começando com 0,
enquanto “C”, “D”, e “E” terão os códigos binários começando com 1, como mostrado na Figura 3.
A divisão seguinte ocorre entre o “A” e “B”, pondo “A” sobre uma folha com código 00 e “B” em
12
uma outra folha com código 01. Após ter executado quatro divisões, os três símbolos os mais
freqüentes têm um código de 2-bits enquanto os símbolos restantes, menos freqüentes têm códigos
de 3-bits.
Figura 3. Exemplo da divisão de Shannon-Fano.
Fonte: Stanford (2006)
A árvore binária que resultante pode ser vista na Figura 4.
Figura 4. Árvore binária resultante de Shannon-Fano.
Fonte: Stanford (2006)
2.3.2 Huffman
A compressão pela codificação de Huffman reduz o tamanho do código usado para
representar os símbolos de um alfabeto. Para Drozdek (2002), o algoritmo de Huffman é
surpreendentemente simples. Símbolos do alfabeto que ocorrem freqüentemente são atribuídos por
códigos mais curtos. A estratégia geral é permitir ao tamanho do código variar de caractere para
caractere e de assegurar que o código utilizado seja menor.
13
A compressão de Huffman é feita através da construção de uma árvore binária usando um
simples conjunto de exemplo. Isso é feito arranjando os símbolos do alfabeto em ordem decrescente
de freqüência. Então repetidamente adicionando duas menores probabilidades e reorganizando. Este
processo se repete até a soma das probabilidades dos dois últimos símbolos serem igual a 1. Uma
vez que este processo seja feito, uma árvore binária de Huffman é possível ser gerada. Se não for
obtida uma probabilidade de 1 nos dois últimos símbolos, provavelmente existe um erro no
processo.
Essa probabilidade de 1 que forma o último símbolo, é a raiz da árvore binária. Os códigos
resultantes são formados traçando a árvore a partir da raiz até os últimos ramos de código depois de
assumir os 0s e 1s as sub-divisões.
Um exemplo do código de Huffman para a codificar as vogais do alfabeto (A, E, I, O, e U)
dadas suas freqüências (A = 0.12, E = 0.42, I = 0.09, O = 0.30 e U = 0.07), seguindo os seguintes
passos:
1. Considerar cada uma das letras como um símbolo com sua probabilidade respectiva;
2. Achar os dois símbolos com a menor probabilidade e combiná-los em um novo
símbolo com ambas as letras adicionando suas probabilidades;
3. Repita a etapa 2 até que haja somente um símbolo à esquerda com uma
probabilidade igual a 1; e
4. Em forma de árvore, cada símbolo deve conter uma única letra ou a dividida em dois
símbolos menores. Todas as folhas as esquerdas da árvore de começar com um 0 e
todas as folhas da direita com um 1. O código para cada uma das letras é a seqüência
de 0 e de 1 que lhe conduzem na árvore, partindo do símbolo com uma probabilidade
de 1.
14
A árvore gerada depois de seguir os passos esta representado na Figura 5.
Figura 5. Exemplo de árvore do código de Huffman.
Fonte: Umich (2006)
Os códigos gerados para cada letra serão: A = 100, E = 0, I = 1011, O = 11 e U = 1010.
Um outro exemplo do código de Huffman para a linguagem “Vowellish” segundo Press
(1992), em forma de árvore. Uma letra (A, E, I, O, ou U) é codificada ou decodificada atravessando
a árvore do alto para baixo. O código é a seqüência de 0 e 1 nas folhas. O valor à direita de cada nó
é sua probabilidade e à esquerda, seu numero de nó na tabela de acompanhando.
Figura 6. Exemplo da árvore binária de Huffman.
Fonte: Press (1992).
15
2.3.3 Ziv e Lempel
Até 1980 a grande maioria dos algoritmos de compressão de dados usavam o modelo
estático de dicionário de tabela. Mas, em 1977 e 1978, Jacob Ziv e Abraham Lempel,
desenvolveram dois métodos de compressão de dados que usavam um dicionário adaptativo. Estes
dois algoritmos deram o inicio a uma explosão de novas técnicas que usavam a base do dicionário
adaptativo para conseguir novas e impressionantes taxas de compressão. (GAILLY, 1995).
2.3.3.1 LZ77
Segundo Gailly (1995), o LZ77 foi primeiro algoritmo de compressão escrito por Ziv e
Lempel, e ele é relativamente fácil em sua implementação. O dicionário consiste de todas as strings
que foram lidas previamente pela mensagem de entrada em uma janela de dados. Um programa de
compressão de arquivos, por exemplo, pode usar uma janela de dados de 4 Kbytes como um
dicionário. Enquanto um novo conjunto de símbolos esta começando a ser lido, o algoritmo procura
por combinações com strings encontradas nos 4 Kbytes de dados previamente lidos.
O algoritmo LZ77 e suas variantes usam uma janela deslizante que se move junto com o
cursor. Segundo Blelloch (2001) a janela pode ser dividida em duas partes, à parte antes do cursor,
chamamos de dicionário, e a parte que começa no cursor, chamamos de look-ahead buffer. Os
tamanhos destas duas partes são parametrizados pelo programa e é fixa durante a execução do
algoritmo. O algoritmo básico é muito simples, executa-se fazendo laços das seguintes etapas:
1. Encontre o maior acerto possível de uma string começando a partir do cursor e que
esteja completamente contida no look-ahead buffer, com uma string que esteja no
começo do dicionário;
2. Output o trio (p, n, c) contendo a posição p da ocorrência na janela, o comprimento n
de um acerto e o próximo caractere c após o acerto; e
3. Mova o cursor n + 1 caracteres para frente.
Na Figura 7 podemos ver um exemplo de LZ77 com um dicionário de tamanho 6 e um look-
ahead buffer de tamanho 4 citado por Blelloch (2001). A posição do cursor é quadriculada, o
dicionário está realçado e o look-ahead buffer está sublinhado. A última etapa não encontra um
grande acerto (10, 3, 1) desde que a string de busca passa do comprimento da janela.
16
Figura 7 Exemplo de janela deslizante do algoritmo LZ77.
Fonte: Blelloch (2001).
A posição p pode ser dada relativa ao cursor com o 0 significando nenhum acerto, e 1
significando um acerto começando no caractere precedente. A Figura 7 mostra um exemplo do
algoritmo no na string “aacaacabcababac”.
Para decodificar a mensagem considera-se uma única etapa. Assume-se que o decodificador
construiu corretamente a string até o cursor atual, e queremos apenas mostrar que dado o trio (p, n,
c) pode-se reconstruir a string até a próxima posição do cursor. Para fazer isso, o decodificador
pode ver a string indo para trás p posições e pegando os próximos n caracteres, seguindo com o
caractere c. Um caso que pode complicar é quando n > p, como na etapa 3 do exemplo na Figura 7.
O problema é a string para copiar as sobreposições do look-ahead buffer, quando o decodificador
ainda não o preencheu. Neste caso o decodificador pode reconstruir a mensagem pegando p
caracteres antes do cursor e ir repetindo quantas vezes necessárias até que o cursor se encha em n
posições. Se, por exemplo, o código fosse (2, 7, d) e os dois caracteres antes do cursor fossem ab, o
algoritmo colocaria “abababa” e então o “d” após o cursor.
Para Gailly (1995), o LZ77 e suas variantes fazem dele um algoritmo de compressão
atrativo, pois a manutenção e a codificação de saída são simples e os programas podem funcionar
muito mais rápidos para codificar e decodificar quando escritos com o algoritmo LZ77. Programas
populares como PKZIP e LHarc usam variantes do algoritmo LZ77.
A outra maneira de explicar o funcionamento do LZ77 é em termos de sua decodificação. A
Figura 8 mostra uma saída do codificador LZ77, supondo que o alfabeto consiste apenas de “a” e
“b”. A saída consiste de triplas. O primeiro componente de uma tripla indica quanto voltar no texto
já decodificado para encontrar a próxima frase, o segundo componente armazena o comprimento
dessa frase, e o terceiro dá o próximo caractere da entrada. Os dois primeiros itens constituem um
ponteiro de volta no texto. Na Figura 8 os caracteres “abaabab” já foram decodificados, e os
próximos caracteres para ser decodificados são representados pela tripla <5,3,b>. Portanto o
17
decodificador volta cinco caracteres no texto decodificado e copia três caracteres, produzindo a
frase “aab”. A próxima tripla, <1,10,a>, é uma referencia recursiva. Para decodificá-la, o
decodificador começa copiando um caractere anterior (b) e copia os próximos dez caracteres. Isso
produzirá 10 b’s consecutivos.
Figura 8. Exemplo da decodificação do LZ77.
Fonte: IME (2006).
2.3.3.2 LZ78
O algoritmo LZ78 faz um exame diferente para construir e manter o dicionário segundo
Gailly (1995). Em vez de ter uma janela de tamanho limitado no texto precedente, LZ78 constrói
seu dicionário fora de todos os símbolos previamente vistos no texto de entrada. Mas em vez de ter
o acesso livre a todos os símbolos strings no texto precedente, um dicionário de strings é construído
caractere por caractere. A primeira vez que a string “Mark” é vista, por exemplo “Ma” é adicionada
ao dicionário. A próxima vez, “Mar” é adicionado. Se “Mark” for vista outra vez, adiciona-se ao
dicionário.
Este procedimento incremental trabalha muito bem em isolar strings freqüentemente usadas
e em adicioná-las à tabela. Ao contrário dos métodos LZ77, as strings em LZ78 podem ser
extremamente longas, o que permite relações de grande compressão.
Zeeh (2003) explica que o LZ78 é um algoritmo de compressão baseado em dicionário que
mantém um dicionário explícito. A saída das strings codificadas pelo algoritmo consiste em dois
elementos: um índice referindo-se ao maior acerto no dicionário de entrada e o primeiro símbolo
que não combina.
Além da saída da string codificada para armazenamento/transmissão, o algoritmo também
adiciona um índice e um símbolo par ao dicionário. Quando um símbolo que ainda não está no
18
dicionário é encontrado, o índice da string codificada tem o valor 0 e adiciona-se o símbolo ao
dicionário. Com este método, o algoritmo constrói gradualmente o dicionário.
Um exemplo do algoritmo LZ78 usando uma string S = “001212121021012101221011”. A
Figura 9 mostra o processo para codificar S.
Segundo Zeeh (2003), na primeira etapa, os 0’s são encontrados e adicionados ao dicionário.
A saída é 00 porque não há nenhum acerto (índice 0) e o primeiro caractere não combinando é 0. O
codificador prossegue então para a segunda posição, encontrando 0, que já está no dicionário. O
próximo caractere 1 ainda não está no dicionário, então o codificador adiciona a string 01 ao
dicionário (uma referência à primeira entrada no dicionário mais o símbolo 1) e faz a saída deste
par. As próximas etapas seguem o mesmo esquema até que o final da entrada da string.
Figura 9. Codificando uma string com LZ78.
Fonte: Zeeh (2003).
O processo de decodificação é mostrado na Figura 10. O decodificador recebe a referência
“0 0”, com o índice 0 indicando que um símbolo n previamente desconhecido (0) precisa ser
adicionado ao dicionário e aos dados descompressados. A próxima string codificada é “1 1”
resultando em uma entrada 01 (uma referência à entrada 1 mais o símbolo 1) que está sendo
adicionado ao dicionário e a string 01 adicionada aos dados descompressados. O decodificador
continua desta maneira até que todas as strings codificadas sejam decodificadas. (ZEEH, 2003)
19
Figura 10. Decodificando uma string com LZ78.
Fonte: Zeeh (2003).
LZ78 era o primeiro dos dois algoritmos de Ziv-Lempel a conseguir o sucesso popular,
devido à adaptação de LZW por Terry Welch, que dá forma ao núcleo do programa de compressão
do UNIX. (GAILLY, 1995)
Segundo Zeeh (2003), o algoritmo LZ78 tem diversas fraquezas e pode ser melhorado.
Primeiramente, o dicionário cresce sem limites. Vários métodos foram introduzidos para impedir
isso, o mais fácil é transformar o dicionário em um dicionário estático uma vez que o dicionário está
cheio ou jogar fora o dicionário e começar um novo do zero. Há também algumas técnicas mais
sofisticadas para impedir que o dicionário cresça demais, que são as variantes do LZ78. O processo
de construção do dicionário do LZ78 gera frases longas razoavelmente tarde no processo. A
construção do dicionário inclui poucas substrings dos dados processados no dicionário. A inclusão
de um símbolo codificado em cada acerto, pode fazer com que o próximo acerto seja o pior do que
poderia ser se for permitido incluir este símbolo.
2.3.4 Comparação dos Algoritmos
Na Tabela 1 pode-se ver uma comparação entre os algoritmos clássicos de compressão de
dados.
Tabela 1. Comparação dos Algoritmos
Shannon-Fano Huffman Ziv e Lempel Método Estatístico Estatístico Dicionário Estrutura interna Árvore binária Árvore binária Tabela Conteúdo Caractere Caractere Caractere/String
20
2.4 Algoritmos Adaptados
Baseando-se nos algoritmos clássicos autores desenvolveram algoritmos adaptados usando o
mesmo principio básico dos anteriores, melhorando-os ou corrigindo pequenos erros.
2.4.1 LZSS
Esta versão do algoritmo LZ77 foi desenvolvida por Storer e por Szymanski em 1982, e
segundo Salomon (2004) melhora o LZ77 em três maneiras:
1. Mantém o look-ahead buffer em uma fila circular;
2. Mantém o buffer da busca (o dicionário) em uma árvore de busca binária; e
3. Cria o símbolo com os dois campos em vez de três.
Uma árvore de busca binária é uma árvore onde a sub-árvore esquerda de cada nó A
contenha os nós menores do que A, e a sub-árvore direita contem os nós maiores do que A. Desde
que os nós da árvore contenham strings, nós primeiramente precisamos saber como comparar duas
strings e decidir qual delas é "maior". Para Salomon (2004) isto é facilmente compreendido
imaginando que as strings apareçam em um dicionário ou em um lexicon, onde sejam classificadas
alfabeticamente. Claramente, a string rote precede a string said desde que r preceda s (mesmo que
a próxima seja a), então nós consideramos rote menor do que said. Isto é chamado lexicographic
order (ordenando strings lexicograficamente).
Salomon (2004) comenta que a maioria dos computadores modernos usam códigos ASCII
para representar os caracteres (embora alguns usem Unicode e alguns mainframe mais velhos da
IBM, do Amdahl, do Fujitsu, e do Siemens usam o código de 8-bit do EBCDIC desenvolvido por
IBM), e no ASCII o código de um espaço em branco precede aqueles das letras, então uma string
que os começa com um espaço serão menores do que toda a string que começa com uma letra. No
geral, a seqüência de ordenação de um computador determina a seqüência dos caracteres arranjados
do menor para o maior. A Figura 11 mostra dois exemplos de árvores de busca binária.
21
Figura 11. Exemplo de arvore binária de LZSS e uma árvore quase balanceada.
Fonte: Salomon (2004).
Observe a diferença entre a árvore quase balanceada na Figura 11a e na enviesada na Figura
11b. Ambas contêm os mesmos 14 nós, mas elas parecem e se comportam muito diferentemente.
Na árvore balanceada todo o nó pode ser encontrado na maioria das vezes dentro de quatro etapas.
Na árvore enviesada até 14 etapas podem ser necessárias. Em um ou outro caso, o número máximo
das etapas necessárias encontrar um nó igual a altura da árvore. Para uma árvore enviesada (que é
realmente a mesma que uma lista encadeada), a altura é o número dos elementos n, para uma árvore
balanceada, a altura é log de n na base 2, um número muito pequeno.
Um exemplo de como uma árvore de busca binária pode ser usada para maximizar a
performance de busca do dicionário, usamos como entrada uma string pequena "sid eastman
clumsily teases sea sick seals". Para manter o exemplo simples, Salomon (2004) supôs uma janela
de 16-byte de buffer para pesquisa seguido por um look-ahead buffer de 5-byte. Depois que os
primeiros 16 + 5 caracteres foram lidos, a janela é [sid east man clum|sily] teases sea sick seals com
a string "teases sea sick seals" esperando para ser lida.
O codificador faz a varredura no buffer de busca, criando doze strings de cinco caracteres,
(doze desde que 16 - 5 + 1 = 12), que são introduzidos na árvore de busca binária, cada um com seu
offset.
22
Figura 12. Buffer de busca do LZSS.
Fonte: Salomon (2004).
O primeiro símbolo no look-ahead buffer é s, então o codificador procurara na árvore por
strings que começam com s. Dois são encontrados, nos offsets 16 e 10, e o primeiro deles, [sid e]
(no offset 16) acontecendo assim uma combinação maior.
Agora, segundo exemplo, o acerto é de tamanho 2, e do símbolo 2-field (16, 2) são emitidos.
O codificador agora tem que “deslizar” a janela duas posições à direita, e atualizar a árvore. A nova
janela é s[id east man clumsi|ly te]ases sea sick seals.
A árvore deve ser atualizada suprimindo as strings sid e e idei a, e introduzindo as novas
strings clums e lumsi. Se uma mais longa, k-letter, string for achada, a janela tem que deslocar k
posições, e a árvore deve ser atualizada suprimindo k strings e adicionando k novas strings.
Salomon (2004) mostra que as k strings a serem suprimidas são as primeiras no buffer de
busca antes da troca, e as k strings a serem adicionadas são as últimas nela após a troca. Um
procedimento simples para atualizar a árvore deve preparar uma string que consiste nas primeiras
cinco letras no buffer de busca, encontrá-la na árvore, e suprimi-la. Então trocar uma posição do
buffer à direita (ou deslocar os dados à esquerda), preparar uma string que consista nas últimas
cinco letras no buffer de busca, e adicione-a a árvore. Este procedimento deve ser repetido k vezes.
Desde que cada atualização, remova e adicione o mesmo número das strings, o tamanho da
árvore nunca muda. A árvore sempre contém os T nós, onde T é o comprimento de um buffer de
busca menos o comprimento do look-ahead buffer mais 1 (T = S - L + 1). A forma da árvore,
entretanto, pode mudar significativamente. Enquanto os nós estão sendo adicionados e suprimidos,
23
a árvore pode mudar sua forma entre uma árvore completamente enviesada (o pior caso para
procurar) e uma balanceada, a forma ideal para procurar strings.
A terceira melhoria do LZSS sobre o algoritmo LZ77 está no símbolo criado pelo
codificador. Um símbolo de LZSS contém apenas um offset e um comprimento. Se nenhum acerto
for encontrado, o codificador emite um código de uncompressed de um próximo símbolo em vez de
um campo com três símbolos (0, 0, ...). Para distinguir entre o símbolo e o código uncompressed,
cada um é precedido por um único bit (um flag).
Na prática, o buffer de busca pode ser alguns milhares de bytes, então o campo offset seria
tipicamente 11-13 bits. O tamanho do look-ahead buffer deve ser selecionado como o tamanho total
de um símbolo seria 16 bits (2 bytes). Para o exemplo, se o tamanho do buffer de busca for 2 kbyte
(2 elevado a 11), então o buffer do look-ahead deve ser 32 bytes (2 elevado a 5). O campo offset
seria 11 e o comprimento do campo, 5 bits (o tamanho do look-ahead buffer). Com esta escolha de
tamanhos de buffer o codificador emitirá o símbolo de 2-byte ou códigos uncompressed de 1-byte
ASCII. Para o tamanho do flag, uma boa idéia prática é coletar oito itens de saída (símbolo e
códigos do ASCII) em um buffer pequeno, a então gravar um byte que consiste nas oito flags,
seguidas pelos oito itens (que são 1 ou 2 bytes cada um).
2.4.1.1 Deficiências
Salomon (2004) aponta algumas deficiências do algoritmo LZ77 e de suas variações. Já foi
comentado que LZ77 usa uma suposição implícita interna para os testes padrões nos dados. As
strings de dados que não satisfazem a esta suposição comprimem deficientemente. Um exemplo
comum é o texto onde uma determinada palavra, "economy", ocorre freqüentemente, mas é
distribuído uniformemente por todo o texto. Quando esta palavra é deslocada no look-ahead buffer,
sua ocorrência precedente pode já ter sido deslocado para fora do buffer de busca. Um algoritmo
melhor conservaria as strings que mais ocorrem no dicionário e não simplesmente as trocaria a toda
hora.
Uma outra desvantagem do LZ77 é o tamanho limitado L do look-ahead buffer. O tamanho
das strings combinadas é limitado a L - 1, mas L deve ser mantido pequeno, desde que o processo
de combinar strings envolve comparar símbolos individuais. Se o tamanho de L fosse o dobro, a
compressão melhoraria, desde que grandes acertos seriam possíveis, mas o codificador seria muito
mais lento ao procurar por acertos maiores. O tamanho S do buffer de busca é limitado também. Um
24
buffer grande de busca produz melhor compressão, mas retarda o codificador, uma vez procurando
pedaços maiores (mesmo com uma árvore de busca binária). Aumentar os tamanhos dos dois
buffers significa também criar um símbolo mais longo, reduzindo desse modo a eficiência da
compressão. Com o símbolo 2-byte, comprimir uma string de dois caracteres em um símbolo resulta
em 2 bytes mais um flag. Escrever os dois caracteres como dois códigos crus do ASCII resulta em 2
bytes mais dois flags, uma diferença muito pequena no tamanho. O codificador deve, em tal caso,
usar a última escolha e escrever os dois caracteres na forma uncompressed, salvando tempo e
desperdiçando apenas um bit. Salomon (2004) diz que o codificador tem um ponto de 2-byte de
ruptura. Com símbolos mais longos, o ponto da ruptura aumenta para 3 bytes.
2.4.2 LZW
Segundo Salomon (2004) o LZW é uma variante do popular algoritmo LZ78, desenvolvido
por Terry Welch em 1984. Sua característica principal está em eliminar o segundo campo de um
símbolo. Um símbolo de LZW consiste apenas em um ponteiro ao dicionário.
Para compreender melhor LZW, deve-se esquecer temporariamente de que o dicionário é
uma árvore, e pensar em um array de strings de tamanho variável. O método de LZW começa
inicializando o dicionário a todos os símbolos no alfabeto. No exemplo comum de símbolos 8-bit,
as primeiras 256 entradas do dicionário (entradas 0 a 255) são ocupadas antes de qualquer dado de
entrada. Como o dicionário é previamente inicializado, o caractere seguinte da entrada será
encontrado sempre no dicionário. Isto é porque um símbolo de LZW pode consistir apenas em um
ponteiro e não tem que conter um código de caractere como em LZ77 e em LZ78.
O princípio de LZW é que o codificador de entrada de símbolos codifique-os um por um e
os acumule em uma string I. Depois que cada símbolo entra é concatenado a I, o dicionário é
procurado pela string I. Depois que I é encontrado no dicionário, o processo continua. Em algum
ponto, ao adicionar o símbolo seguinte x faz com que a busca falhe, a string I está no dicionário,
mas a string Ix (o símbolo x concatenado a I) não está. Neste momento o codificador:
1. Grava o ponteiro do dicionário que aponta à string I;
2. Guarda a string Ix (que é chamada agora uma frase) na próxima entrada do dicionário
disponível; e
3. Inicializa a string I com o símbolo x.
25
Para ilustrar este processo, usando a string de texto "sir sid eastman easily teases sea sick
seals". As etapas são as seguintes:
•••• Inicializa as entradas 0-255 do dicionário a todos os 256 bytes 8-bit;
•••• O primeiro símbolo entrado s e é encontrado no dicionário (na entrada 115, desde que
este é o código ASCII de s). O próximo símbolo entrado i, mas si não é encontrado no
dicionário. O codificador executa as seguintes etapas: (1) Grava 115 no dicionário; (2)
Guarda a string si na próxima entrada do dicionário disponível (entrada 256) e (3)
Inicializa I com o símbolo i; e
•••• É feita a entrada do r do sir, mas a string ir não está no dicionário. O codificador
executa: (1) Grava 105 (o código do ASCII de i); (2) Guarda a string ir na próxima
entrada do dicionário disponível (entrada 257), e (3) Inicializa I ao símbolo r.
Outra maneira para entender o funcionamento de LZW é primeiramente inicializando o
dicionário com todas os caracteres de entrada possíveis os quais devem ser de 0 à 255 para um
conjunto de caracteres de 8-bit, por exemplo. Agora o dicionário contém todo o alfabeto básico,
assim toda string entrada pode ser representada usando apenas o dicionário de índices. Então, esses
índices são os únicos componentes no arquivo de saída. O procedimento de codificação, como
mostrado na Figura 13, é claramente simples. Seja w uma substring do arquivo de entrada, e c um
caractere imediatamente seguinte à w no arquivo. Se uma ocorrência da string corrente “wc”, a qual
está sendo compactada agora, é encontrada no dicionário, adiciona-se um novo caractere do arquivo
de entrada para a string “wc+novastring”. Isto forma um novo par “wc” e a busca por combinações
continua. Quando não podemos encontrar uma ocorrência de “wc” no dicionário, “wc” é inserido no
dicionário. Então o índice do dicionário correspondente à “w” é enviado ao arquivo de saída, e “c” é
usado como o caractere de início da próxima string a ser codificada.
26
Figura 13. O coração do código do LZW. Codificador e decodificador são mostrados
Fonte: Fundao (2006)
O processo de decodificação, o qual é o reverso do processo de codificação, trabalha por
uma tabela buscando o apontador do dicionário da palavra-código entrada. Ao mesmo tempo, o
dicionário cresce de forma idêntica àquele do codificador. Pode parecer impossível para o
decodificador manter o mesmo dicionário já que o codificador não o transmitiu explicitamente.
Contudo, a virtude da propriedade do prefixo do dicionário garante esta possibilidade. Esta
propriedade limita o número de caracteres que podem ser enviados para o decodificador em um
código, e o decodificador pode deterministicamente predizer o dicionário do codificador conforme
as palavras-código entradas.
O algoritmo original LZW não definia explicitamente o algoritmo de busca no
procedimento de comparação. Implementando com algoritmos de busca em tabelas de hash ou
estruturas de árvore, LZW pode alcançar efetiva velocidade de compressão. Entretanto, ele é
comumente usado em armazenagem ou aplicações de compressão de gráficos.
2.4.3 Burrows-Wheeler
Segundo Salomon (2004) a maioria dos métodos de compressão operam no modo
streaming, onde o codificador lê um byte ou diversos bytes, os processa, e continua até que o final
do arquivo (end of file) seja detectado. O método de Burrows-Wheeler (BW), trabalha em uma
27
modalidade de bloco, onde a stream de entrada é lida bloco por bloco e cada bloco seja codificado
separadamente como uma string. O método é referenciado também como à ordenação por bloco. O
método de BW tem como finalidade geral, trabalhar em imagens, em sons, e em texto, e pode
conseguir altas relações de compressão (1 bit por byte ou até melhor).
A idéia principal do método de BW é começar com uma string S de n símbolos e scramble
em uma outra string L que satisfaça a duas seguintes circunstâncias:
1. Toda a região de L tenderá a ter uma concentração apenas de alguns símbolos. Uma
outra maneira de dizer isto é, se um símbolo s for encontrado em uma determinada
posição em L, então será provável encontrar outras ocorrências próximas de S. Esta
propriedade significa que L possa ser comprimida facilmente e eficientemente
usando o método “mova para frente”, talvez em combinação com RLE. Isto significa
também que o método de BW trabalhará bem somente se n for grande (ao menos
muitos milhares de símbolos por string); e
2. Seja possível reconstruir a string original S de L (alguns dados a mais podem ser
requeridos para a reconstrução, além de L, mas não muito).
O termo matemático para símbolos scrambling é permutação, e é fácil mostrar que uma
string de símbolos de n tem n! permutações. Este é um número grande mesmo para valores
relativamente pequenos de n, então a permutação usada por BW tem que ser selecionada com
cuidado. O codificador de BW procede nas seguintes etapas:
1. A string L é criada, pelo codificador, como uma permutação de S. Alguma
informação, denotada por I, é criada também, para ser usada mais tarde pelo
decodificador na etapa 3.
2. O codificador comprime L e I e escreve os resultados em uma stream de
saída. Esta etapa tipicamente começa com RLE, continua com o código mova
para frente (move-to-front), e finalmente aplicam o código de Huffman.
3. O decodificador lê a stream de saída e decodifica-o aplicando os mesmos
métodos que no passo 2 mas na ordem reversa. O resultado é a string L e a
variável I.
28
4. L e I são usados pelo decodificador para reconstruir a string original S.
A primeira etapa é compreender como a string L é criada de S, e que informação necessita
ser armazenada em I para poder reconstruir mais tarde. Para ilustrar este processo Salomon (2004)
usa a string “swiss miss”.
Figura 14. Exemplo de codificação usando Burrows-Wheeler.
Fonte: Salomon (2004).
O codificador constrói uma matriz n x n onde armazena a string S na fileira superior,
seguida por n – 1 cópias de S, a cada troca cíclica um símbolo à esquerda é posicionado (Figura
14a). A matriz é classificada então lexicograficamente, produzindo a matriz ordenada da Figura
14b. Observando cada fileira e cada coluna de cada uma das duas matrizes são uma permutação de
S e contêm assim todos os n símbolos de S. A permutação L selecionada pelo codificador é a última
coluna da matriz classificada. No exemplo esta é a string "swm siisss". Uma única informação para
eventual reconstrução de S a partir de L a mais, é o número da fileira da string original na matriz
classificada, que no exemplo é 8 (a fileira e a coluna começam de 0). Este número é armazenado em
I.
Para ver porque L contém concentrações de símbolos idênticos, supõe-se que as palavras
bail, fail, hail, jail, mail, nail, pail, rail, sail, tail, and wail apareçam em algum lugar em S. Após a
classificação, todas as permutações que começam com il ficarão juntas. Todas contribuem um a
para L, então L terá uma concentração dos “a”. Também, todas as permutações que começam com
ail terminarão juntas, contribuindo a uma concentração das letras bfhjmnprstw em uma região de
L.
29
Pode-se agora caracterizar o método de BW dizendo que esse método usa a classificação
para agrupar junto os símbolos baseados em seus contextos. Entretanto, o método considera o
contexto em somente um lado de cada símbolo.
A última coluna, L, da matriz classificada contém concentrações de caractere idênticos, que
é porque L é fácil de comprimir. Entretanto, a primeira coluna, F, da mesma matriz é ainda mais
fácil de comprimir, desde que contenha funcionamentos, não apenas concentrações, de caracteres
idênticos. Seleciona-se a coluna L e não a coluna F porque a string original S pode ser reconstruída
de L e não de F.
O decodificador segundo Salomon (2004) lê uma stream comprimida, e decodifica-a usando
Huffman e mova para frente (move-to-front) e talvez RLE, e então reconstrói a string S a partir de
L descomprimida em três etapas:
1. A primeira coluna da matriz classificada (coluna F na Figura 14c) é
construída de L. Este é um processo direto, desde que F e L contenham os
mesmos símbolos (ambas são permutações de S) e F é classificado. O
decodificador classifica simplesmente a string L para obter F;
2. Ao classificar L, o decodificador prepara uma disposição auxiliar T que
mostre as relações entre elementos de L e F (Figura 14c). O primeiro
elemento de T é 4, implicando que o primeiro símbolo de L (a letra "s")
está situado em posição 4 de F. O segundo elemento de T é 9, implicando
que o segundo símbolo de L (a letra "w") está situado em posição 9 de F, e
assim por diante. Os índices de T no exemplo são (4, 9, 3, 0, 5, 1, 2, 6, 7,
8); e
3. A string F não é mais utilizada. O decodificador usa L, I, e T para
reconstruir S de acordo com S[n − 1 − i] ← L[Ti[I]], for i = 0, 1, . . . , n −
1, onde T0[j] = j, and Ti+1[j] = T[Ti[j]].
Um exemplo onde quanto maior o tamanho dos blocos segundo FLI (2006), maior a taxa de
compressão atingida. Os parâmetros de entrada são, um vetor A de tamanho N com os dados a
serem transformados e os parâmetros de retorno são, um vetor B (permutação dos elementos de A)
e um índice k.
30
Inicialmente, são geradas todas as N rotações à direita possíveis para o vetor A, formando
uma matriz N x N. A seguir, ordena-se estas N linhas e copia-se o último caractere de cada uma
para o vetor B. Atribuímos a k o número da linha da matriz ordenada que corresponde à A.
Codificando a string, "mana cana banana":
• Criando a matriz N x N
Figura 15.Codificando uma string com BW.
• Após ordenação da matriz:
31
Figura 16. Matriz resultante do processo de codificação pelo método BW.
Temos B = [aaannncmnb aaaaa] (coluna 16 da matriz) e k = 12
2.5 Software Livre
Software Livre é o software disponível com a permissão para qualquer usuário usá-lo, copiá-
lo, e distribuí-lo, seja na sua forma original ou com modificações. Em especial, a possibilidade de
modificações implica em que o código fonte esteja disponível. É importante não confundir software
livre com software grátis porque um software livre pode-se copiar, modificar e redistribuir
independe de gratuidade. Existem programas que podem ser obtidos gratuitamente, mas que não
podem ser modificados, nem redistribuídos. (HEXSEL, 2006)
2.6 Propriedade Intelectual
Todo software é criado a partir de atividade intelectual, e como tal, é protegido por um
conjunto de leis que tratam de propriedade intelectual, ou copyright. O conceito principal do
copyright é que o autor original do trabalho determina a forma pela qual sua obra será utilizada.
Mais especificamente, copyright permite ao autor determinar quais serão os direitos de uso, cópia,
modificação e distribuição (incluindo aluguel, empréstimo e transmissão), entre outros
(ENGELFRIET, 2002).
32
A organização que consolida as diretrizes internacionais de copyright é a World Intellectual
Property Organization sengundo Wipo (2006). O copyright tem um prazo de validade, após o qual o
uso da obra torna-se de domínio público, ou seja, não tem restrição alguma de uso. As condições e
direitos específicos do autor são determinados pela legislação de copyright de cada país.
2.7 Licenças de Software
O uso de licenças de software para designar a forma como um software pode ser usado é
bastante comum. A licença é um documento (não necessariamente registrado ou validado com
órgão ou organização), que determina as condições de uso que o software poderá ser utilizado. Para
a elaboração de tal documento, é seguido em termos especificados pelas leis de copyright.
(ENGELFRIET, 2006).
2.7.1.1 Graus de Restrição em licenças de Softwares
Segundo Hexsel (2002) os softwares podem ser classificados como:
• Open Source: é um software de utilização livre, para quem desejar. E todos podem
contribuir com ele, seja no seu desenvolvimento, na correção de erros, na
documentação, desde que o código permaneça aberto;
• Software de Domínio Público: é software sem copyright. Alguns tipos de cópia, ou
versões modificadas, podem não ser livres porque o autor permite que restrições
adicionais sejam impostas na redistribuição do original ou de trabalhos derivados;
• Freeware: é usado em programas que permitem a redistribuição, mas não a
modificação, e seu código fonte não é necessariamente disponibilizado. Estes
programas não são softwares livre;
• Shareware: é o software disponibilizado com a permissão para que seja
redistribuído, mas a sua utilização implica no pagamento pela sua licença.
Geralmente, o código fonte não é disponibilizado e, portanto modificações são
impossíveis;
• Software Proprietário: é aquele cuja cópia, redistribuição ou modificação são
proibidas pelo seu proprietário. Para usar, copiar ou redistribuir deve-se solicitar
permissão ao proprietário, ou pagar para poder fazê-lo; e
33
• Software Comercial: é o software desenvolvido por uma empresa com o objetivo de
lucrar com sua utilização. A maioria do software comercial é proprietário, mas existe
software livre que é comercial, e existe software não-livre não-comercial.
Este projeto se caracteriza como Open source e Freeware. Sendo Open source o código fonte
do núcleo principal que será de livre acesso para contribuições futuras e Freeware a interface
disponibilizada com a primeira versão do sistema.
2.8 Ferramentas Similares
Com a grande necessidade de estar enviando dados pela Internet ou armazenando-os de
forma mais compacta, existem várias ferramentas de compressão no mercado. Porém 90% dessas
ferramentas são softwares proprietários e pagos. Nenhum dos softwares analisados era multi-
plataforma, apenas eram executados no sistema operacional Microsoft Windows.
Serão analisados na seqüência duas ferramentas pagas (WinRar e WinZip) e uma ferramenta
livre (7Zip). Todos os softwares analisados usam o método de compressão de dados sem perdas de
informação.
2.8.1.1 WinRar
O WinRar é uma ferramenta de compressão de dados paga, desenvolvida pela Rarlab. Com
esta ferramenta pode-se criar/manipular arquivos compactados através de uma interface como
mostra a Figura 17 (WINRAR, 2006).
34
Figura 17. Tela principal do WinRar.
Fonte: Winrar (2006).
2.8.1.2 WinZip
O Winzip é um dos softwares pioneiros para compactação de arquivos para o sistema
operacional Microsoft Windows. Sua licença de uso é paga e é desenvolvida pela WinZip
Internacional LLC. Pode-se ver na Figura 18 a interface do WinZip, onde-se pode cirar/manipular
arquivos compactados.
Figura 18. Tela principal do WinZip.
Fonte: Winzip (2006).
35
O 7Zip é um dos poucos softwares de compressão de dados não pagos para o sistema
operacional Microsoft Windows. Essa ferramenta é desenvolvida por um projeto de Igor Pavlov. A
Figura 19 mostra a interface da ferramenta 7Zip:
Figura 19. Tela principal do 7Zip.
Fonte: 7zip (2006)
36
2.8.1.3 Principais funções nas ferramentas
As ferramentas similares estudadas já possuem um certo tempo de mercado, e por isso as
principais funções são semelhantes, mudando algum detalhe ou interface para se fazer a mesma
operação.
Algumas principais funções são:
• Compactar e descompactar arquivos;
• Possibilidade de navegar em um arquivo compactado;
• Adicionar e retirar parte dos arquivos contidos em um arquivo compactado;
• Proteger o arquivo compactado com senha;
• Integração com a Shell do sistema operacional; e
• Autoextract de arquivos compactados.
2.8.1.4 Comparação entre as ferramentas
Na
37
Tabela 2 pode-se observar a comparação entes às ferramentas similares estudadas. Foram
levantados quais são os algoritmos que cada ferramenta usa e qual é a sua performance em
compactar arquivos de diferentes tipos.
38
Tabela 2. Ferramentas Similares
WinZip WinRar 7Zip Algoritmos LZH + LZW + SF + Huff +
PPMd F + LZ77 + PPMII + Huff F + LZMA + PPMII +
LZ77 + BWT Licença de uso Shareware Shareware Freeware Interface gráfica Sim Sim Sim Taxa de Compressão de Texto (2,988,578 bytes)
84.51% 85.16% 85.19%
Taxa de Compressão de Imagem (4,149,414 bytes)
80.97% 80.98% 81.58%
Taxa de Compressão de PDF (4,526,946 bytes)
17.29% 17.51% 18.26%
Taxa de Compressão de executável (3,870,784 bytes)
59.82% 66.82% 67.43%
Fonte: Adaptado de Maximun (2006).
Pode-se observar pela tabela acima que todas as ferramentas similares estudadas tem em
comum o uso dos mesmos algoritmos clássicos de compressão, variantes ou adaptações. Os
programas usam filtros, dicionários externos e/ou pré-processamento do arquivo (F), algoritmo de
Huffman (Huff), algoritmo de Shannon-Fano (SF), algoritmo de Lempel-Ziv (LZ), algoritmo de
transformação de Burrows-Wheeler (BWT) e no algoritmo de predição por acerto parcial (PPM).
Por usarem os mesmos algoritmos, pode-se observar também que a taxa de compressão de
diversos tipos de arquivos praticamente são os mesmos, sendo de menos de três pontos percentuais
para cima ou para baixo a diferença entre as ferramentas.
Podemos observar na Tabela 3 que mesmo codificando arquivos de tamanhos diferentes e de
formatos diferentes a taxa de compressão ainda são semelhantes.
39
Tabela 3. Ferramentas similares codificando diversos arquivos.
WinZip WinRar 7Zip Arquivo Codificado (tamanho em bytes)
986.771,05 867.602,46 872.473,54
Taxa de Compressão 68.81% 72.58% 72.42%
Fonte: Adaptado de Maximun (2006).
O arquivo codificado continha 46 tipos diferentes de arquivos num total de 510 arquivos,
totalizando 316.355.757 bytes. O tamanho médio dos arquivos é de 620,305 bytes sendo o maior de
18,403,071 bytes e o menor de 3,554 bytes.
40
3 PROJETO
Neste trabalho desenvolveu-se uma ferramenta de compressão de dados open source. A
ferramenta tem como objetivo suprir a necessidade de compressão de arquivos, gerenciando-os
desde a sua criação, edição, bem como a visualização da informação contida no mesmo.
A ferramenta de compressão de dados é voltada para todos os usuários de computador que
necessitem de um Software que auxilie na criação de arquivos que ocupem menor tamanho em
meio de armazenamento, e que possibilite um ganho em tempo ao transmitir esse dado via Internet.
Essa ferramenta é composta de dois executáveis independentes, um núcleo principal
responsável por toda a manipulação de arquivos codificados e de uma interface que é responsável
pela interação do usuário com o núcleo.
O núcleo recebe informação através de trocas de mensagens com a interface ou através de
linha de comandos enviados pelo prompt do sistema operacional.
É importante ressaltar que o núcleo principal foi desenvolvido em C ANSI e a interface de
interação foi desenvolvida para o sistema operacional Microsoft Windows em Pascal da ferramenta
Borland Delphi, deixando em aberto para futuros trabalhos o desenvolvimento de outras interfaces
ou adicionando novos métodos ao núcleo.
3.1 FERRAMENTA DE COMPRESSÃO DE DADOS OPEN SOURCE
A ferramenta foi desenvolvida usando os métodos de compressão de dados sem perda, e os
métodos foram o LZW e o BW + RLE. A escolha da utilização desses métodos foi tomada por base
nas pesquisas das ferramentas similares, por serem amplamente usadas e por proporcionarem
resultados de compressão satisfatórios.
A ferramenta de compressão de dados foi baseada em algumas das principais
funcionalidades das ferramentas já existentes, mas possibilitará criar interfaces de interação com o
núcleo principal para qualquer sistema operacional.
O sistema é executado na plataforma Microsoft Windows e utiliza como base as
funcionalidades da ferramenta WinZip. Atualmente, o WinZip é uma ferramenta de funcionamento
41
simples, conforme mostra a Tabela 4, a qual exemplifica as etapas para a criação de um arquivo
compactado.
Tabela 4. Descrição das etapas de criação de um arquivo compactado no WinZip
1. O usuário clica em novo arquivo e escolhe um nome para o arquivo a ser criado. 2. Na tela que será aberta, o usuário escolherá quais arquivos serão compactados. 3. Ao clicar em no botão “Adicionar”, o Software compactará os arquivos previamente
selecionados. 4. Uma vez criado o arquivo, o usuário pode adicionar ou remover arquivos para o arquivo
compactado. 5. Para restaurá-los basta clicar no botão “Extrair”, voltando os arquivos ao tamanho original.
3.1.1 Implementação do Método LZW
O método LZW foi implementado pelo norte americano Michael Dipperson. Para esse
trabalho, suas implementações sofreram pequenos ajustes para trabalhar com a interface
desenvolvida.
Como o LZSS (LZ77), o algoritmo LZW usa um dicionário dinâmico de strings e codifica
as strings em "code Word" fazendo uma referencia a uma string no dicionário. O dicionário do
LZW não é um dicionário externo que lista todos os símbolos conhecidos. Ao invés, o dicionário é
inicializado com uma entrada para cada byte possível. Outras strings são incluídas ao passo que são
lidas da origem dos dados. O "code Word" para a string é simplesmente o próximo valor disponível
uma vez que é adicionada ao dicionário.
Uma string codificada é usada para adicionar novas strings ao dicionário, sendo construída
lendo 1 byte por vez da entrada de dados. Se a string formada pela junção do novo byte lido com a
string codificada já está no dicionário, o novo byte é incluído ao final da string codificada. Se não
estiver no dicionário, é criada uma nova entrada no dicionário para a nova string e um "code Word"
para a string codificada é escrito para a saída de dados. Então a string codificada é setada com o
byte que acabou de ser lido.
Os passos para se codificar usando o método LZW são:
1. Inicializar o dicionário para que o mesmo contenha uma entrada para cada byte.
Inicializar a string codificada com o primeiro byte da entrada de dados;
2. Ler o próximo byte da entrada de dados;
42
3. Se o byte lido for EOF vá para o passo 6;
4. Se concatenando o byte com a string codificada produz uma string que já esteja no
dicionário: concatena-se o byte com a string codificada; vá para o passo 2;
5. Se concatenando o byte com a string codificada produz uma string que não esteja no
dicionário: adiciona-se a nova string ao dicionário; escreve-se um "code Word" para
a string codificada na saída de dados; seta-se a string codificada igual ao novo byte
vá para o passo 2; e
6. Gera-se a saída com os "code Word" das strings codificadas e termina.
Exemplo: "this_is_his_thing"
Figura 20. Codificando uma stringusando o método LZW.
No exemplo, uma string de 17 caracteres agora é representada com apenas 13 "code words".
Os dados codificados pelo LZW são decodificados ao contrario do que são codificados. O
dicionário é inicializado contendo uma entrada para cada byte. Ao invés de manter uma string
43
codificada, o ultimo "code word" e o primeiro caractere da string codificada são mantidos. Novos
"code word" são lidos da entrada de dados um por vez e a string codificada pelo novo "code word"
é decodificada.
Durante o processo de codificação, o código anterior ao code word atual é escrito porque
concatenado o primeiro caractere do code word atual com a string codificada pelo code word
anterior gerou um code word que não estava no dicionário. Quando isso acontece a string formada
pela concatenação foi adicionada ao dicionário. A mesma string precisa ser adicionado ao
dicionário na hora de decodificar.
Os passos para se decodificar usando o método LZW são:
1. Inicializar o dicionário para que contenha uma entrada para cada byte;
2. Ler o primeiro code word da entrada de dados e escrever o byte que ela representa;
3. Ler o próximo code word da entrada de dados;
4. Se o code word é EOF então termina;
5. Escreve a saída da string codificada pelo code word;
6. Concatena-se o primeiro caractere do novo code word lido com a string produzida
pelo code word anterior e adiciona a string resultante no dicionário; e
7. Vá para o passo 3.
Exemplo: Decodificando a frase 't' 'h' 'i' 's' '_' 258 '_' 257 259 256 'i' 'n' 'g'.
Nas tabelas abaixo pode-se ver as principais funções desse método.
Tabela 5. Função Principal do Método LZW.
Função main
Descrição Valida os comandos da linha de comando e chama as funções encode/decode.
Parâmetros argc - quantidade de parâmetros, argv - lista dos parâmetros.
Efeitos Codifica/Decodifica um arquivo.
Retorno EXIT_SUCCESS para sucesso, senão EXIT_FAILURE.
44
Tabela 6. Função Para Decodificar em LZW.
Função LZWDecodeFile
Descrição Essa rotina lê de um arquivo uma string codificada por vez e decodifica.
Parâmetros inFile - nome do arquivo a ser decodificado, outFile - nome do arquivo destino.
Efeitos Decodifica um arquivo.
Retorno TRUE para sucesso, senão FALSE.
Tabela 7. Função Para Codificar em LZW.
Função LZWEncodeFile
Descrição Essa rotina lê um arquivo um caractere por vez e o codifica.
Parâmetros inFile - nome do arquivo a ser codificado, outFile - nome do arquivo destino.
Efeitos Codifica um arquivo.
Retorno TRUE para sucesso, senão FALSE.
3.1.2 Implementação do Método BW + RLE
O método de Burrows Wheeler faz uma “ordenação” em blocos de dados para que esses
dados fiquem de forma que aumente a performance de outros métodos como Move to Front ou o
RLE.
Para codificar o método faz uma permutação nos dados recebidos, organizando-os de uma
forma que seja possível voltar aos dados originais, mas aumentando a eficácia do RLE por
”agrupar” ocorrências iguais.
Os passos para codificar um bloco são:
1. Permutar os dados do bloco;
2. Setar como string resultante a ultima ocorrência de cada permutação.
O método BW possui decodificação mais trabalhosa, abaixo segue os passos para a
decodificação:
1. Para o tamanho do bloco de dados repetir o procedimento;
2. Inserir o caractere lido como uma coluna nova embaixo a esquerda em uma tabela;
45
3. Ordenar as linhas da tabela alfabeticamente; e
4. Retornar a linha que acaba com "EOF".
O Run length encoding se difere dos outros métodos de compressão. Ele não tenta reduzir o
número de símbolos necessários para representar certa informação como Huffman por exemplo, e
não substitui strings usando um dicionário como Lemple-Ziv e Lemple-Ziv-Welch. RLE substitui
uma string de símbolos repetidos com um único símbolo e um contador (run length) indicando o
numero de vezes que o símbolo se repete.
Exemplo: "aaaabbcdeeeeefghhhij" pode ser representada como "a4b2c1d1e5f1g1h3i1j1". Os
números que estão em negrito indicam que eles são valores, não símbolos.
Os passos para codificar uma string são:
1. Setar o símbolo anterior igual a um valor qualquer;
2. Ler o próximo símbolo da entrada;
3. Se for EOF então termina;
4. Escrever o símbolo atual;
5. Se o símbolo não for igual ao anterior, seta-se o símbolo anterior com o símbolo
atual e vai para o Passo 2;
6. Ler e incrementar o contador até ser lido um símbolo diferente do atual;
7. Escrever o contador;
8. Escrever o símbolo que não combinou com o símbolo anterior; e
9. Setar o símbolo anterior com o símbolo que não combinou, ir para o Passo 2 .
Os passos pra decodificar uma string são:
1. Setar o símbolo anterior igual a um valor qualquer;
2. Ler o próximo símbolo da entrada;
46
3. Se for EOF então termina;
4. Escrever o símbolo atual;
5. Se o símbolo não for igual ao anterior, seta-se o símbolo anterior com o símbolo
atual e vai para o Passo 2;
6. Ler a quantidade que o símbolo se repete;
7. Escrever o símbolo a quantidade de vezes que ele se repete; e
8. Vai para o Passo 1.
O método BW + RLE implementado consiste em ler um arquivo bloco por bloco (cada
bloco com 100 caracteres), até o final do arquivo. A cada bloco lido, passa pela ordenação dos
caracteres pelo método BW, em seguida a string resultante é codificada pelo método RLE e desse
procedimento é guardado em um arquivo resultante os dados codificados.
Para fazer a decodificação desse arquivo, se utiliza o caminho inverso: lê-se todo o arquivo
codificado e decodifica-o usando método de RLE, da string resultante, lê-se bloco por bloco e
decodifica-o pelo método BW, assim, restaurando o arquivo original.
Tabela 8. Função Principal do Método Híbrido BW + LRE.
Função main
Descrição Valida os comandos da linha de comando e chama as funções encode/decode.
Parâmetros argc - quantidade de parâmetros, argv - lista dos parâmetros.
Efeitos Codifica/Decodifica um arquivo.
Retorno EXIT_SUCCESS para sucesso, senão EXIT_FAILURE.
Tabela 9. Função que Codifica um Bloco de Dados Usando BW.
Função bwEncode
Descrição Recebe um bloco de dados e efetua a codificação.
Parâmetros Str – lista de caracteres.
Efeitos Codifica um bloco de caracteres.
Retorno Retorna uma string resultante da codificação.
47
Tabela 10. Função que Codifica um Bloco de Dados Usando BW.
Função bwDecode
Descrição Recebe uma string de caracteres e efetua a decodificação.
Parâmetros Str – lista de caracteres.
Efeitos Decodifica uma string.
Retorno Retorna uma string resultante da decodificação.
Tabela 11. Função que Codifica uma String de Caracteres Usando RLE.
Função rleEncode
Descrição Recebe uma string de caracteres e efetua a codificação.
Parâmetros Str – lista de caracteres.
Efeitos Codifica uma lista de caracteres.
Retorno Retorna uma string resultante da codificação.
Tabela 12. Função que Decodifica uma String de Caracteres Usando RLE.
Função rleDecode
Descrição Recebe uma string de caracteres e efetua a decodificação.
Parâmetros Str – lista de caracteres.
Efeitos Decodifica uma lista de caracteres.
Retorno Retorna uma string resultante da codificação.
4 TECNOLOGIAS UTILIZADAS
Para o desenvolvimento do projeto, foram utilizadas as seguintes tecnologias: Borland
Delphi, C ANSI (Software) e PHP (webpage).
As linguagens escolhidas foram definidas levando-se em conta as suas atribuições,
experiência e conhecimento do desenvolvedor, sendo o Delphi necessário para desenvolvimento da
Interface para Microsoft Windows e o C ANSI para desenvolvimento do núcleo, em uma linguagem
onde possa ser portada e compilada para qualquer plataforma.
4.1.1 Borland Delphi
48
O Delphi, buscando velocidade no desenvolvimento e na execução dos aplicativos, possui
um ambiente de desenvolvimento fácil de usar, com uma grande Biblioteca de Componentes
Visuais, a VCL (Visual Component Library). Esta Biblioteca contém código de botões, campos,
rótulos, gráficos, caixas de diálogo e acesso e tabelas de bancos de dados.
O rápido desenvolvimento de aplicativos é possível graças aos vários controles disponíveis
na paleta de componentes, onde o programador escolhe um destes componentes, e coloca-o
diretamente no local desejado, dentro de um formulário.
Vantagens de se trabalhar com o Delphi:
• Facilidade em alterações e implementações;
• Melhor Estruturação do código;
• Velocidade; e
• Verdadeira orientação a objetos.
4.1.2 C++
A linguagem C++ foi desenvolvida inicialmente por Bjarne Stroustrup, na AT&T, de 1979 a
1983, à partir da linguagem C, tendo como idéia principal a de agregar o conceito de orientação à
objetos, àquela linguagem.
Bjarne procurou tanto quanto possível manter retrocompatibilidade com C, de modo que
programas feitos nessa linguagem pudessem ser compilados por um compilador C++, com o
mínimo de alterações.
C++ oferece um conjunto de mecanismos básicos que não estam presentes no C, e estes
devem ser usados para produzir software mais modular e confiável, explorando-se as verificações
disponíveis no compilador.
Finalmente, os mecanismos de C++ inspiraram a programação segundo o paradigma de
orientação à objetos.
49
4.1.3 PHP
PHP (Hypertext Preprocessor) é uma linguagem de script Open Source de uso geral, muito
utilizada e especialmente guarnecida para o desenvolvimento de aplicações Web embutível dentro
do HTML.
O código PHP é delimitado por tags iniciais e finais que lhe permitem ativar ou não a
interpretação do PHP.
O que distingui o PHP do Javascript, o código é executado no servidor, de maneira que o
cliente recebe os resultados da execução, sem nenhum modo de determinar como é o código fonte.
5 DESENVOLVIMENTO
5.1 Modelagem do Sistema
O sistema foi modelado seguindo as orientações da UML e a modelagem foi realizada
utilizando a ferramenta Enterprise Architect versão 5.0 da Sparx Systems. A modelagem do sistema
proposto foi desenvolvida utilizando os seguintes diagramas: diagrama use-case e diagrama de
seqüência.
O sistema conta com duas partes, a primeira é a interface com o usuário e a segunda é o
núcleo principal contendo todas as instruções para realizar a compressão e descompressão dos
arquivos.
Já o núcleo contendo todas as instruções é o encarregado de pegar os arquivos e comprimi-
los, usando as técnicas de compressões derivadas dos métodos de Ziv e Lempel e Burrows Wheller,
pois traz mais possibilidades de modificações e adaptações, e é utilizado pela grande maioria das
ferramentas similares disponíveis no mercado, gerando um arquivo binário final capaz de ser
reconstruído ao formato original.
5.1.1 DIAGRAMAS DE CASOS DE USO (USE CASE)
Foram levantados dois casos de uso para o sistema, o primeiro é a operação de compressão e
a segunda é a operação de descompressão realizada pelo usuário.
50
Figura 21. Caso de Uso do Sistema.
Na compressão, conforme a Figura 22 mostra, temos:
• Escolhe arquivos: o usuário escolhe os arquivos para serem codificados pelo núcleo
principal;
• Escolhe destino: o usuário escolhe o destino onde o arquivo já codificado irá ser
gravado;
• Escolhe nome: o usuário escolhe o nome do novo arquivo codificado;
• Escolhe tipo: o usuário escolhe qual tipo de arquivo irá ser criado, um compatível
com o formato ZIP ou um com formato do próprio sistema; e
• Manda gerar arquivo final: o usuário solicita a codificação dos arquivos selecionados
e receberá uma mensagem com o status final da operação.
51
Figura 22. Cenário para o Caso de Uso para criar um arquivo codificado.
Na descompressão, conforme a Figura 23, temos:
• Escolhe arquivo: o usuário escolhe qual arquivo já codificado ele irá decodificar;
• Escolhe sub-arquivos: o usuário pode optar em decodificar todos os arquivos
contidos no arquivo codificado ou escolher somente alguns;
• Escolhe destino: o usuário escolherá o destino onde os arquivos decodificados serão
gravados; e
• Manda gerar saída: o usuário irá mandar gerar os arquivos decodificados para o
núcleo e receberá uma mensagem com o status final da operação.
52
Figura 23. Cenário para o Caso de Uso para decodificar um arquivo
codificado.
5.1.2 DIAGRAMA DE SEQUÊNCIA
A interação entre Interface e núcleo principal se da através da troca de mensagens entre um e
outro. Esta interação do usuário com o sistema seguirá os seguintes passos:
1. O usuário irá informar quais arquivos serão codificados/decodificados, selecionará as
configurações necessárias à operação, como local onde serão gravados os novos
arquivos gerados, nome e o tipo do arquivo;
2. A interface do sistema enviará para o núcleo essas informações previamente
informadas pelo usuário;
3. O núcleo receberá essas informações, e irá realizar todo o procedimento de
compressão/descompressão dos arquivos selecionados;
4. Após ter codificado os arquivos, o núcleo devolverá a interface uma mensagem
informando se a operação foi realizada com sucesso ou se houve algum tipo de erro;
5. A interface será encarregada de informar ao usuário o status da operação realizada.
53
Para melhor compreensão, a Figura 24 ilustra o diagrama de seqüencia do sistema de
compressão.
Figura 24. Diagrama de seqüência do sistema.
A compressão em si será realizada da seguinte forma:
• Os dados de entrada serão analisados;
• O método ideal será escolhido com base na análise previamente realizada, ou seja, o
método de melhor performance e que é usado nas principais ferramentas similares;
• O núcleo aplicara o método de compressão nos arquivos selecionados.
5.2 TELAS DO SISTEMA
O sistema está dividido em duas partes, a Interface e o núcleo principal. O núcleo será um
executável do tipo “linha de comando”, portanto não terá telas. Já a interface terá três telas
principais.
54
É através da interface que o usuário interage com o sistema, podendo criar/editar os arquivos
a serem codificados ou decodificados, informar o local onde deverá ser criado o novo arquivo,
enfim, todas as operações que serão realizadas pelo núcleo passarão pela Interface.
A Figura 25 mostra a tela principal da Interface. Nela, o usuário encontrará todas as funções
disponíveis no sistema, tais como codificação, decodificação e configurações.
Figura 25. Tela principal da Interface.
A Figura 26 mostra a tela de compressão dos arquivos. O usuário irá escolher os arquivos
que deseja codificar e algumas configurações.
Figura 26. Tela de compressão da Interface.
55
A Figura 27 mostra a tela final para a codificação. O usuário informa o nome final do
arquivo que a ferramenta irá gerar, codificando os arquivos escolhidos pelo usuário. O formato do
arquivo final poderá ser escolhido entre o formato proprietário da ferramenta (DPS) ou pelo formato
padrão de codificação de outras ferramentas (ZIP). Como opção, o usuário poderá escolher excluir
os arquivos de origem após codificá-los e poderá proteger arquivo codificado com uma senha de
segurança.
Figura 27. Tela final da codificação.
A Figura 28 mostra a tela de descompressão dos arquivos. O usuário irá informar o arquivo
codificado e o caminho do destino dos arquivos que serão decodificados, no caso desse caminho
não existir a ferramenta irá sugerir a criação do mesmo.
56
Figura 28. Tela de descompressão da Interface.
5.3 MÉTODOS
O algoritmo de codificação e decodificação baseou-se nos métodos LZW, LZS e BW. Os
algoritmos LZW e LZS são simétrico, adaptativo e sem perdas, utilizam a estratégia de compressão
baseada em dicionário (tabela de associação código - símbolo). A filosofia destes métodos é de
substituir seqüências de símbolos por códigos. Em geral, os códigos são menores que as seqüências
que representam, implicando em uma redução do tamanho.
O LZW e LZS são algoritmos de compressão de arquivos bastante utilizados devido ao fato
de levarem em consideração cadeias semelhantes de caracteres do arquivo, além de ter um custo
computacional bastante conveniente, principalmente por não precisar de espaço extra de
armazenamento nem consumir muita memória.
A filosofia do BW é reordenar a seqüência de caracteres de uma maneira que aumente a
performance de métodos como o RLE.
57
5.4 COMUNICAÇÃO ENTRE APLICATIVOS
A comunicação entre a Interface e o núcleo, foi feita por passagem de parâmetros na linha
de comando para a execução do núcleo. Quando a interface chama o núcleo para que o mesmo faça
a codificação/decodificação ele passa as informações necessárias via parâmetro.
5.5 TESTES E RESULTADOS
O sistema está em fase de testes em um ambiente real e funcional rodando sobre o Microsoft
Windows XP. Nesta fase de testes, foram realizados testes de codificação e decodificação de
arquivos.
A Erro! A origem da referência não foi encontrada. mostra os resultados obtidos nos
testes. O sistema pode ser acessado no endereço https://www.deividps.com/tcc .
Tabela 13. Comparação entre ferramentas.
WinZip WinRar 7Zip DPS (LZW) DPS (BW + RLE)
Arquivo Word (51.5 KB)
8.92 KB (17.32%)
8.16 KB (15.84%)
7.25 KB (14.07%)
15.8 KB (30.67%)
39.7 KB (77,8%)
Arquivo Texto (5.94 KB)
2.8 KB (47.13%)
2.8 KB (47.13%)
2.83 KB (47.64%)
3.28 KB (55.21%)
4.23 KB (72.21%)
Arquivo Imagem (194 KB)
62.2 KB (32.06%)
61.3 KB (31.59%)
51.7 KB (26.64%)
68.6 KB (35.36%)
83.54 KB (43.06%)
Conforme os dados da Tabela 13, pode-se analisar o resultado obtido da codificação feita
pelas ferramentas, sendo o tamanho resultante do arquivo codificado e a porcentagem do tamanho
do arquivo original.
Após o uso do sistema, foi observado que as funcionalidades definidas no projeto de
desenvolvimento executaram de forma correta. O sistema foi utilizado por alguns usuários leigos,
que emitiram opiniões favoráveis quanto à simplicidade e entendimento do funcionamento do
sistema, até quando comparado com ferramentas similares. Outros testes ainda serão realizados, já
que o sistema será utilizado no dia a dia para a realização do gerenciamento de arquivos
codificados.
58
6 CONCLUSÕES
O estudo realizado neste trabalho possibilitou uma ampla compreensão da complexidade dos
algoritmos e softwares de compressão de dados. Na fase de pesquisa levou-se em consideração todo
o estudo sobre os algoritmos clássicos, suas derivações e adaptações. Todos os objetivos propostos
para esta fase foram atingidos, o que proporcionou uma visão geral dos procedimentos envolvidos
no processo de criação de dados codificados.
Já na fase da análise das ferramentas existentes, observou-se uma quantidade considerável
de ferramentas disponíveis, mas que, em grande maioria são ferramentas pagas e não oferecem
portabilidade. Das três ferramentas analisadas duas eram Softwares pagos, enquanto o outro é um
projeto Freeware de Igor Pavlov, mas que não tem portabilidade, sendo executado apenas em
Microsoft Windows.
O sistema deste projeto foi desenvolvido com base nas pesquisas realizadas, e em outros
projetos similares. Ele objetivou o gerenciamento de arquivos codificados, sendo sua interface com
o núcleo principal, desenvolvido para Microsoft Windows. De modo que o resultado alcançado foi
satisfatório, criando o núcleo independente da interface e gerando um arquivo de menor tamanho
que o original, sendo possível a sua descompressão.
Do ponto de vista acadêmico, este trabalho foi uma ótima oportunidade de demonstrar na
prática os conhecimentos obtidos durante o Curso de Ciência da Computação, possibilitando aplicar
os conteúdos vistos durante as disciplinas e com a complementação de pesquisas.
Como sugestão para novos estudos baseados nessa proposta, propõe-se o desenvolvimento
de uma Interface de comunicação com o núcleo e a adição de novos métodos de compressão ao
núcleo.
O resultado deste trabalho poderá ser aperfeiçoado através da adição de outras rotinas de
compressão para a codificação de novos métodos.
59
REFERÊNCIAS BIBLIOGRÁFICAS
7-ZIP. “7-Zip is a file archiver with high compression ratio”. Disponível em “http://www.7-zip.org/”. Acessado em 09/06/2006. BLACK, Paul E. “Dictionary of Algorithms and Data Structures” Disponível em: “http://www.nist.gov/dads/” Acessado em: 19/05/2006.
BLELLOCH, Guy E. Introduction to Data Compression. Pittsburgh, 2001. DROZDEK, Adam. Estrutura de dados e algoritmos em C++. Thomson, São Paulo, 2002.
ENGELFRIET, A. “Choosing a software license”. Disponível em: “http://www.iusmentis.com/computerprograms/licenses/pcactive0203/” Acessodo em 12/06/2006.
ENGELFRIET, A. “Crash course on copyright”. Disponível em: “http://www.iusmentis.com/copyright/crashcourse/”. Acessado em 13/06/2006.
FLI. “Algoritmo Burrows-Wheeler” Disponível em: “http://www.ime.usp.br/~fli/bwt.html” Acessado em: 31/07/2006.
FUNDAO. “Fundão da Computação - Algoritmos - LZW” Disponível em: “http://www2.fundao.pro.br/articles.asp?cod=24” Acessado em: 31/07/2006.
GAILLY, Jean Loup. The Data Compression Book. M&T Books , New York, 1995.
HEXSEL, Roberto A. Propostas de ações de governo para incentivar o uso de software livre. Relatório Técnico do Departamento de Informática da UFPR, 004/2002, out 2002.
HEXSEL, Roberto A. “O que é Software Livre?” Disponível em: “http://www.softwarelivre.gov.br/SwLivre/” Acessado em: 19/05/2006.
IME, “Mac 499- Trabalho De Formatura Supervisionado” Disponível em:
“http://www.linux.ime.usp.br/~cef/mac499-01/monografias/ulisses” Acessado em 31/07/2006
LARSSON , Jesper N. Structures of String Matching and Data Compression. Lund University, Lund, 1999. MAXIMUM. “Lossless data compression software benchmarks / comparisons”. Disponível em “http://www.maximumcompression.com/”. Acessado em 10/06/2006. PINHEIRO, José Maurício Santos. “Projeto e Gestão de Redes de Computadores”. Disponível em “http://www.projetoderedes.com.br/artigos/“. Acessado em 11/05/2006. PRESS, William H. Numerical Recipes In C: The Art Of Scientific Computing. Cambridge, 1992. SALOMON, David. Data Compression. Springer, Nova York, 2004.
60
SOUSA, Lindeberg Barros De. Redes De Computadores: Dados, Voz E Imagem. Érica, São
Paulo, 2002.
STANFORD, “Lossless Data Compression: Shannon-Fano” Disponível em:
“http://cse.stanford.edu/class/sophomore-college/projects-00/data-
compression/lossless/shannonfano/example.htm” Acessado em: 31/07/2006
UMICH, “Encoding - Huffman Coding” Disponível em:
“http://www.si.umich.edu/Classes/540/Readings/Encoding - Huffman Coding.htm” Acessado em
31/07/2006.
ZEEH, Christina. The Lempel Ziv Algorithm. Artigo do seminário Famous Algorithms 16/01/2003. ZIVIANI, Nivio. Projeto de algoritmos. Thomson, São Paulo, 2004. WINRAR. “WinRAR archiver, a powerful tool to process RAR and ZIP files”. Disponível em “http://www.rarlab.com/”. Acessado em 09/06/2006. WINZIP. “WinZip - The Zip File Utility for Windows”. Disponível em “http://www.winzip.com”. Acessado em 09/06/2006.
WIPO. “World intellectual property organization” Disponível em: “http://www.wipo.int/portal/index.html” Acessado em: 12/06/2006.
WOLFRAM, Stephen. A New Kind of Science. USA, 2002