В данном проекте реализован архиватор посредством алгоритма Хаффмана.
Программа-архиватор имеет следующий интерфейс командной строки:
archiver -c archive_name file1 [file2 ...]- заархивировать файлыfil1, file2, ...и сохранить результат в файлarchive_name.archiver -d archive_name- разархивировать файлы из архиваarchive_nameи положить в текущую директорию. Имена файлов должны сохраняться при архивации и разархивации.archiver -h- вывести справку по использованию программы.
Алгоритм сжатия устроен следующим образом:
- Подсчитывается частотность 8-битных символов в файле. Кроме содержимого файла надо учесть частоты символов в имени файла, а также добавить три служебных символа
FILENAME_END=256,ONE_MORE_FILE=257,ARCHIVE_END=258с частотами 1. Назначение этих символов будет описано позже. Таким образом, для кодирования расширенного алфавита необходимо 9 бит. - Строится бинарный бор кодирования следующей процедурой:
- Для каждого символа алфавита добавляется соответствующая вершина в очередь с приоритетом. Упорядочение вершин в очереди осуществляется по неубыванию частот символов в файле (в "начале" очереди всегда вершина с символом с наименьшей встречаемостью в файле).
- Пока в очереди больше одного элемента, из нее последовательно извлекаются две вершины A и B с минимальными приоритетами. Создается новая вершина С, детьми которой являются вершины A и B. Вершина C помещается в очередь с приоритетом, равным сумме приоритетов вершин A и B.
- По окончанию процедуры в очереди остается ровно одна вершина, которая является корнем построенного бора. Листовые вершины являются терминальными. В каждой терминальной вершине записан символ из исходного файла. Каждая нетерминальная вершина дерева содержит два ребра: левое и правое, которые помечаются битами 0 и 1 соответственно. Каждой терминальной вершине соответствует битовая последовательность, получающаяся спуском из корня бора к терминальной вершине и выписыванием битов всех пройденных ребер. Для наглядности можно воспользоваться следующими иллюстрациями:
- Всем символам ставится в соответствие бинарная кодовая последовательность посредством построенного бора.
- Код приводится к канонической форме. Каноническая форма кода отличается тем, что позволяет однозначно восстановить коды по списку символов и длинам кодов для них. Алгоритм восстановления канонического кода есть в википедии.
- Все символы файла заменяются на соответствующие кодовые бинарные последовательности, и результ записывается вместе со вспомогательной информацией в файл. Формат файла архива описан ниже.
Алгоритм декодирования в целом обратен алгоритму кодирования и устроен следующим образом:
- Из файла восстанавливается таблица кодирования (соответствие между сиволами и их кодами).
- По таблице кодирования строится бинарный бор.
- По бинарным последовательностям, прочитанным из входного файла, производится трассировка по бору от корня к листовым вершинам. При достижении очередной листовой вершины бора определяется соответсвующий ей символ, который записывается в выходной файл.
Девятибитные значения записываются в формате "от младшего к старшему" (аналог little-endian для битов). Т.е. сначала идет бит, соответствующий 2^0, затем - 2^1 и т.д. до бита, соответствующего 2^8.
Внутри unsigned char биты считаются от младшего к старшему. Чтобы программа была портируемой, нельзя полагаться на то, что порядок битов в файле соответствует порядку битов в бинарном представлении чисел в памяти.
Пример: дана последовательность unsigned char {1, 3, 7, }. Требуется прочитать из неё два 9-битных значения a и b.
Представление на битовом уровне:
10000000 11000000 11100000
aaaaaaaa abbbbbbb bb
Следовательно, a = 257, b = 385.
Файл с архивом имеет следующий формат:
- 9 бит - количество символов в алфавите
SYMBOLS_COUNT - Блок данных для восстановления канонического кода
SYMBOLS_COUNTзначений по 9 бит - символы алфавита в порядке следования канонических кодов- Список из
MAX_SYMBOL_CODE_SIZEзначений по 9 бит, i-й (при нумерации с 0) элемент которого - это количество символов с длиной кодаi+1.MAX_SYMBOL_CODE_SIZE- максимальная длина кода в текущем кодировании. Канонические коды соответствуют по порядку символам из предыдущего пункта.MAX_SYMBOL_CODE_SIZEне записывается явным образом в файл, т.к. его можно восстановить по имеющимся данным.
- Закодированное имя файла
- Закодированный служебный символ
FILENAME_END - Закодированное содержимое файла
- Если в архиве есть ещё фалы, то закодированный служебный символ
ONE_MORE_FILEи кодировка продолжается с п.1. - Закодированный служебный символ
ARCHIVE_END.
Все компоненты программы по возможности создавались более универсальными и не привязанными к специфике конкретной задачи. Например, алгоритмы кодирования и декодирования работают с потоками ввода-вывода, а не файлами.
Програма корректно обрабатывает очень большие (во много раз превосходящие по объему оперативную память) файлы.