Алгоритм сжатия LZW. Почему в консоль выводится мусор после 256 кода?
Есть такая строка в файле
abacabadabacabae
Я формирую из нее отдельные "слова" по алгоритму сжатия без потерь LZW . Словарь изначально заполнен ASCII кодами и их значениями в виде структуры.
typedef struct Word {
unsigned int code;
char * value;
} Word;
Изначально заполняю словарь
// свободный код
unsigned int freeCode = 256;
// Переменная, где будем хранить текущий код символа из входного файла
int currentCharCode;
// Словарь состоит из 4096 слов
Word * dictionary [4096];
// Префикс и суффикс текущего слова
Word prefix;
Word sufix;
// Начальная инициализация словаря
// Значения Суффикса и префикса будем хранить в виде С-строк, чтобы иметь возможность использовать
функции strcat() strcpy()
// значению суффикса выделяем 2 байта
sufix.value = checkNull(malloc(2* sizeof(char)));
sufix.value [0] = 0;
sufix.value [1] = '\0';
dictionary[0] = checkNull(malloc(sizeof(Word)));
dictionary[0]->value = checkNull(malloc(2* sizeof(char)));
strcpy (dictionary[0]->value, sufix.value);
dictionary [0] ->code = 0;
sufix.value = checkNull(malloc(2* sizeof(char)));
for (int i = 0; i < 256; i++){
// Переводим ASCII В C-строку, записываем ее в словарь
sufix.value [0] = i;
sufix.value [1] = '\0';
dictionary[i] = checkNull(malloc(sizeof(Word)));
dictionary[i]->value = checkNull(malloc(2* sizeof(char)));
strcpy (dictionary[i]->value, sufix.value);
dictionary [i] ->code = i;
}
// После инициализации в словаре хранятся строки "[ASCII-код]\0"
free(sufix.value);
// Записываем в префикс с-строку [ASCII-код]\0 первого символа из входного потока
currentCharCode = fgetc(inFile);
prefix.value = checkNull(malloc(2*sizeof(char)));
prefix.value[0] = currentCharCode;
prefix.value[1] = '\0';
prefix.code = currentCharCode;
// будем использовать переменную для проверки есть ли слово в словаре
int inDictionary =1;
Основной цикл сжатия:
// пока не конец файла Переприсваиваем currenCharCode код нового символа
printf("compressed file codes:\r\n");
while ((currentCharCode = fgetc(inFile)) != EOF ){
// формируем суффикс
sufix.value = checkNull(malloc(2*sizeof(char)));
sufix.value[0] = currentCharCode;
sufix.value[1] = '\0';
sufix.code = currentCharCode;
if (strlen(prefix.value) <= strlen (dictionary[freeCode-1]->value) && inDictionary==1) realloc(prefix.value, strlen(prefix.value) + 2);
if (prefix.value == NULL) prefix.value = strcpy(prefix.value, sufix.value);
// К нашему текущему префиксу добавляем суффикс Теперь слово у нас расширилось на один символ
else prefix.value = strcat (prefix.value, sufix.value);
// Проверяем словарь
for (int i = 0; i< freeCode; i++){
// если мы найдем в словаре наш префикс, то коду префикса назначаем соответствующий код из словаря
if (strcmp (prefix.value, dictionary[i]->value) == 0){
prefix.code = dictionary[i]->code;
inDictionary = 1;
}
else {
inDictionary = 0;
}
if (inDictionary==0 && i ==freeCode-1 && strlen (prefix.value+1)!=0){
printf ("%d\t%d\r\n", prefix.code, freeCode);
//Если выполнилось условие проверки, то это значит, что расширенного слова нет в нашем словаре, значит
// Выводим в выходной поток код слова, которое есть в словаре
fwrite (&prefix.code, sizeof(int), 1, outFile);
// выделяем память под слово с первым свободным кодом
dictionary [freeCode] = checkNull(malloc(sizeof(Word)));
// выделяем память под значение нового слова в словаре
dictionary [freeCode]-> value = checkNull(malloc(strlen(prefix.value) + 1));
// копируем новое слово в словарь
dictionary[freeCode] -> value = strcpy (dictionary [freeCode]->value, prefix.value);
// Назначаем ему код
prefix.code = freeCode;
dictionary[freeCode]->code = prefix.code;
// Мы записали расширенное в словарь
// Теперь текущим словом должен стать суффикс расширенного слова
strcpy (prefix.value, sufix.value);
prefix.code = sufix.code;
free (sufix.value);
// и увеличиваю код свободного слова
++freeCode;
inDictionary =1;
}
}
}
fclose (inFile);
fclose (outFile);
}
Вот вывод в консоль из основного цикла сжатия:
Тут я вывел в консоль код "предыдущего" слова в файле и код в "нового" слова в словаре.
Функция корректно сжимает строку abacab дальше в файл почему-то выводится мусор (смотреть первый столбик).
Почему так происходит?
Вот полный код моей программы для тех, кому нетрудно помочь с отладкой. Я постарался снабдить комментариями всю функцию сжатия.
Заранее спасибо.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lzwlib.h"
// слова будем хранить в структуре
typedef struct Word {
unsigned int code;
char * value;
} Word;
void * checkNull(void * ptr){
if (ptr==0){
printf("%s","out of memory");
exit (37);
}
return ptr;
}
// функция сжатия принимает на вход путь к файлу, который надо сжать
FILE * compress (const char* inputFileName) {
// Далее идет код, в котором формирую имя файла с расширением .lzw, наш выходной поток
//...
char * outputFileName = checkNull(malloc(strlen (inputFileName)+5));
strcpy (outputFileName, inputFileName);
char * ex = strchr (outputFileName, '.');
if (ex) strcpy (ex, ".lzw");
else strcat (outputFileName, ".lzw");
// Теперь открываем наши файлы
FILE* outFile;
outFile = fopen (outputFileName, "wb");
FILE* inFile;
inFile = fopen (inputFileName, "r");
free (outputFileName);
// свободный код
unsigned int freeCode = 256;
// Переменная, где будем хранить текущий код символа из входного файла
int currentCharCode;
// Словарь состоит из 4096 слов
Word * dictionary [4096];
// Префикс и суффикс текущего слова
Word prefix;
Word sufix;
// Начальная инициализация словаря
// Значения Суффикса и префикса будем хранить в виде С-строк, чтобы иметь возможность использовать функции strcat() strcpy()
// значению суффикса выделяем 2 байта
sufix.value = checkNull(malloc(2* sizeof(char)));
sufix.value [0] = 0;
sufix.value [1] = '\0';
dictionary[0] = checkNull(malloc(sizeof(Word)));
dictionary[0]->value = checkNull(malloc(2* sizeof(char)));
strcpy (dictionary[0]->value, sufix.value);
dictionary [0] ->code = 0;
sufix.value = checkNull(malloc(2* sizeof(char)));
for (int i = 0; i < 256; i++){
// Переводим ASCII В C-строку, записываем ее в словарь
sufix.value [0] = i;
sufix.value [1] = '\0';
dictionary[i] = checkNull(malloc(sizeof(Word)));
dictionary[i]->value = checkNull(malloc(2* sizeof(char)));
strcpy (dictionary[i]->value, sufix.value);
dictionary [i] ->code = i;
}
// После инициализации в словаре хранятся строки "[ASCII-код]\0"
free(sufix.value);
// Записываем в префикс с-строку [ASCII-код]\0 первого символа из входного потока
currentCharCode = fgetc(inFile);
prefix.value = checkNull(malloc(2*sizeof(char)));
prefix.value[0] = currentCharCode;
prefix.value[1] = '\0';
prefix.code = currentCharCode;
// будем использовать переменную для проверки есть ли слово в словаре
int inDictionary =1;
// пока не конец файла Переприсваиваем currenCharCode код нового символа
printf("compressed file codes:\r\n");
while ((currentCharCode = fgetc(inFile)) != EOF ){
// формируем суффикс
sufix.value = checkNull(malloc(2*sizeof(char)));
sufix.value[0] = currentCharCode;
sufix.value[1] = '\0';
sufix.code = currentCharCode;
printf ("%d-%s\t", prefix.code, prefix.value);
if (strlen(prefix.value) <= strlen (dictionary[freeCode-1]->value) && inDictionary==1) realloc(prefix.value, strlen(prefix.value) + 2);
if (prefix.value == NULL) prefix.value = strcpy(prefix.value, sufix.value);
// К нашему текущему префиксу добавляем суффикс Теперь слово у нас расширилось на один символ
else prefix.value = strcat (prefix.value, sufix.value);
// Проверяем словарь
for (int i = 0; i< freeCode; i++){
// если мы найдем в словаре наш префикс, то коду префикса назначаем соответствующий код из словаря
if (strcmp (prefix.value, dictionary[i]->value) == 0){
prefix.code = dictionary[i]->code;
inDictionary = 1;
}
else {
inDictionary = 0;
}
if (inDictionary==0 && i ==freeCode-1 && strlen (prefix.value+1)!=0){
//Если выполнилось условие проверки, то это значит, что расширенного слова нет в нашем словаре, значит
// Выводим в выходной поток код слова, которое есть в словаре
fwrite (&prefix.code, sizeof(int), 1, outFile);
// выделяем память под слово с первым свободным кодом
dictionary [freeCode] = checkNull(malloc(sizeof(Word)));
// выделяем память под значение нового слова в словаре
dictionary [freeCode]-> value = checkNull(malloc(strlen(prefix.value) + 1));
// копируем новое слово в словарь
dictionary[freeCode] -> value = strcpy (dictionary [freeCode]->value, prefix.value);
// Назначаем ему код
prefix.code = freeCode;
dictionary[freeCode]->code = prefix.code;
// Мы записали расширенное в словарь
printf("%d-%s\r\n", dictionary[freeCode]->code, dictionary[freeCode]->value);
// Теперь текущим словом должен стать суффикс расширенного слова
strcpy (prefix.value, sufix.value);
prefix.code = sufix.code;
free (sufix.value);
// и увеличиваю код свободного слова
++freeCode;
inDictionary =1;
}
}
}
fclose (inFile);
fclose (outFile);
}
FILE * decompress (const char* inputFileName){
const char * ex2 = ".lzw";
if (strstr(inputFileName, ex2)==0 || strchr(inputFileName, '.')==0){
printf ("%s", "incorrect file");
exit(38);
}
char * outputFileName = checkNull(malloc (strlen(inputFileName)+1));
strcpy (outputFileName, inputFileName);
char * ex = strchr(outputFileName, '.');
if (ex) strcpy (ex, "2.txt");
else strcat (outputFileName, "2.txt");
// Теперь открываем наши файлы
FILE* outFile;
outFile = fopen (outputFileName, "rw");
FILE* inFile;
inFile = fopen (inputFileName, "r");
free (outputFileName);
unsigned int freeCode = 256;
int currentCharCode;
Word * dictionary [4096];
Word prefix;
Word sufix;
// Начальная инициализация словаря
// Значения Суффикса и префикса будем хранить в виде С-строк, чтобы иметь возможность использовать функции strcat() strcpy()
// значению суффикса выделяем 2 байта
sufix.value = checkNull(malloc(2* sizeof(char)));
sufix.value [0] = 0;
sufix.value [1] = '\0';
dictionary[0] = checkNull(malloc(sizeof(Word)));
dictionary[0]->value = checkNull(malloc(2* sizeof(char)));
strcpy (dictionary[0]->value, sufix.value);
dictionary [0] ->code = 0;
sufix.value = checkNull(malloc(2* sizeof(char)));
for (int i = 0; i < 256; i++){
// Переводим ASCII В C-строку, записываем ее в словарь
sufix.value [0] = i;
sufix.value [1] = '\0';
dictionary[i] = checkNull(malloc(sizeof(Word)));
dictionary[i]->value = checkNull(malloc(2* sizeof(char)));
strcpy (dictionary[i]->value, sufix.value);
dictionary [i] ->code = i;
}
// После инициализации в словаре хранятся строки "[ASCII-код]\0"
free(sufix.value);
int codeInFile =0;
printf ("%s\r\n", "decompress codes:");
while (!feof(inFile)){
fread(&codeInFile, sizeof(int), 1, inFile);
if (codeInFile < freeCode){
sufix.value = checkNull(malloc (2* sizeof(char)));
sufix.value [0] = codeInFile;
sufix.value [1] ='\0';
realloc(dictionary[freeCode]->value, strlen(dictionary[freeCode]->value+2));
if (dictionary[freeCode]->value == NULL){
strcpy(dictionary[freeCode]->value, sufix.value);
free(sufix.value);
}
else{
strcat(dictionary[freeCode]->value, sufix.value);
free(sufix.value);
}
}
else if (freeCode <= codeInFile){
fprintf(outFile, "%s", dictionary[freeCode++]->value);
}
printf("%d-%s\t%d-%s\r\n", codeInFile, dictionary[codeInFile]->value, freeCode, dictionary[freeCode]->value);
}
fclose(inFile);
fclose(outFile);
}
// тестирую сжатие
int main (){
char * s = "toCompress.txt";
char * s2 = "toCompress.lzw";
FILE* compressed = compress(s);
FILE* decompressed = decompress(s2);
return 0;
}
