Вопрос № 178449: Здравствуйте. В среде турбо паскаль нужно написать программу-распаковщик текста по лагоритму LZW c использованием динамического стека. Структура программы: модуль работы со словарем, модуль распаковщик, вызывающая их программа. Программа должна счит...
Вопрос № 178449:
Здравствуйте. В среде турбо паскаль нужно написать программу-распаковщик текста по лагоритму LZW c использованием динамического стека. Структура программы: модуль работы со словарем, модуль распаковщик, вызывающая их программа. Программа должна считать закодированный файл, вывести на экран полученный текст и создать выходной файл.Заранее спасибо.
Отвечает amnick, Студент :
Здравствуйте, Федоров Иван .
Ниже приведен код модуля LZW-распаковщика (простейший неадаптивный вариант), а код программы распаковки, использующей этот модуль — в приложении.
LZW_UNP.PAS
Код:
{********************************************************** Модуль распаковки
файла, сжатого алгоритмом LZW. Перевод на Паскаль: LZW data compression/expansion demonstration program. Mark R. Nelson From Dr.Dobb's Journal (Oct, 1989) by MacSoft **********************************************************}
unit LZW_UNP;
interface
type { процедура для информирования вызывающей программы о процессе распаковки } userInfoProc = procedure( n: longint ); { n - число "распакованных" байт }
type a_word = array[0..0] of word; a_byte = array[0..0] of byte; PByte = ^a_byte;
var prefix_code : ^a_word; { указатель на массив для префиксных кодов } append_character : ^a_byte; { указатель на массив для добавляемых символов } dec
ode_stack : PByte; { указатель на массив для декодированной строки } stack_size : word;
const EOF = $FFFF; { признак конца файла }
{ Вспомогательная функция: чтение одного символа из файла. Возвращает прочитанный символ в младшем байте слова или EOF при достижении конца файла или ошибке. } function getc( var f: file ) : word; var b: byte; nRead : word; begin BlockRead( f, b, 1, nRead ); if nRead = 0 then getc := EOF else getc := b; end;
{
Вспомогательная процедура: запись одного байта в файл. Контроля на ошибки нет. } procedure putc( b: byte; var f: file ); var nWritten : word; begin BlockWrite( f, b, 1, nWritten ); end;
const input_bit_count : integer = 0; { число битов во входном буфере } input_bit_buffer : longint = 0; { буфер для считанных из запакованного файла битов }
{ нам нужно будет извлекать биты посредством битового сдвига вправо,
но в Turbo Pascal нет беззнаковых длинных целых, поэтому используем absolute для доступа к старшему слову input_bit_buffer как input_bit_array[1] } var input_bit_array : array[0..1] of word absolute input_bit_buffer;
{------------------------------------------------------------------ Функция возвращает очередной битовый код из запакованного файла. ------------------------------------------------------------------} function input_code( var input: file ): word; begin {
пока в буфере есть место для очередного байта...} while input_bit_count <= 24 do begin {...помещаем следующий байт из файла справа от ранее прочитанных битов } input_bit_buffer := input_bit_buffer or ( longint(getc(input)) shl (24-input_bit_count) );
{ Возвращаемое значение (очередной код) - старшие BITS битов из буфера. Этот код должен быть возвращен в младших
битах результата, поэтому сдвигаем его вправо: input_code := input_bit_buffer shr (32-BITS); Но так делать нельзя, поскол
ьку при сдвиге вправо идет заполнение знаковым битом, поэтому берем эти BITS битов из старшего слова } input_code := input_bit_array[1] shr (16-BITS);
{ удаляем прочитанный код из буфера } input_bit_buffer := input_bit_buffer shl BITS;
{------------------------------------------------------------------ Эта функция просто декодирует строку по переданному коду, сохраняя ее в стеке. Затем
стек выводится в обратном порядке процедурой распаковки (expand). Возвращает указатель на вершину стека (первый символ строки). ------------------------------------------------------------------} function decode_string( stack_top: integer; code: word ): integer; var p : PByte; begin while code > 255 do begin { помещаем очередной символ на вершину стека } decode_stack^[stack_top] := append_character^[code]; inc( stack_top ); { пр
одвигаем указатель стека }
code := prefix_code^[code]; { проверим, есть ли место в стеке для очередного символа } if stack_top >= stack_size then begin getmem( p, stack_size+1024 ); move( decode_stack^, p^, stack_size ); freemem( decode_stack, stack_size ); decode_stack := p; inc( stack_size, 1024 ); end; end; decode_stack^[stack_top] := code; decode_string := stack_top; end;
{ Процедура распаковки файла, сжатого алгоритмом LZW } procedure
expand( var input, output: file; userInfo: userInfoProc ); var next_code, new_code, old_code: word; character : byte; counter : longint; stack_top : integer; { индекс вершины стека; должен быть знаковым } begin { инициализируем распаковщик } getmem( prefix_code, TABLE_SIZE*sizeof(word) ); getmem( append_character, TABLE_SIZE*sizeof(byte)); stack_size := 1024; getmem( decode_stack, 1024 );
next_code := 256; {
первый доступный код } counter := 0; { счетчик для показа процесса распаковки } userinfo( 0 ); { информируем вызывающую программу о начале распаковки }
old_code := input_code( input ); { первый код } character := old_code; { это всегда первый символ исходного файла } putc( old_code, output ); { записываем его в файл } inc( counter );
new_code := input_code(input); { читаем очередной код из файла } while new_code <> MAX_VALUE do begin
{ пока не распаковали все ... } { Следующий код проверяет специальный случай STRING+CHARACTER+STRING+CHARACTER+STRING при котором генерируется неизвестный код. } if new_code >= next_code then begin decode_stack^[0] := character; stack_top := decode_string( 1, old_code ); end { иначе мы просто декодируем очередной код } else stack_top := decode_string( 0, new_code );
{ Выводим декодированную строку из
стека в файл. } character := decode_stack^[stack_top]; while stack_top >= 0 do begin { записываем символ с вершины стека в файл } putc( decode_stack^[stack_top], output ); inc( counter ); if (counter and $FFF) = 0 then { каждые 4 килобайта } userInfo( counter ); { информируем вызывающую программу }
{ выкидываем символ с вершины посредством продвижения указателя вниз } dec( stack_top ); end;
{ заполняем таблицы, пока есть место - аналогично
LZW-компрессору } if next_code <= MAX_CODE then begin prefix_code^[next_code] := old_code; append_character^[next_code] := character; inc( next_code ); end; old_code := new_code; new_code := input_code(input); { читаем очередной код из файла } end;
В Turbo Pascal нельзя индексировать указатели, поэтому для обхода этого
ограничения отключается контроль индексов, объявляются типы
a_word = array[0..0] of word a_byte = array[0..0] of byte;
и используются указатели, например:
var append_character : ^a_byte;
После этого можно индексировать: append_character^[i]. (Здесь индексируется не указатель, а массив из одного элемента, на который он указывает. Благодаря отключенной проверке индексов мы получаем то, что требуется.) Этот метод позволяет избежать статического объявления довольно больших (для
реального режима процессора) массивов и эффективнее использовать память.
В программе практически нет контроля ошибок.
В задании требуется сделать отдельно модуль работы со словарем и модуль распаковщик. Если приведенного модуля недостаточно и его требуется разбить на два, то обращайтесь в минифорум.
Отлажено и протестировано в Borland Pascal 7.0 Успехов!
Приложение:
Ответ отправил: amnick, Студент
Ответ отправлен: 21.05.2010, 16:05
Номер ответа: 261550
Вам помог ответ? Пожалуйста, поблагодарите эксперта за это! Как сказать этому эксперту "спасибо"?
Отправить SMS#thank 261550
на номер 1151 (Россия) |
Еще номера »
Оценить выпуск »
Нам очень важно Ваше мнение об этом выпуске рассылки!
* Стоимость одного СМС-сообщения от 7.15 руб. и зависит от оператора сотовой связи.
(полный список тарифов)
** При ошибочном вводе номера ответа или текста #thank услуга считается оказанной, денежные средства не возвращаются.
*** Сумма выплаты эксперту-автору ответа расчитывается из суммы перечислений на портал от биллинговой компании.