1
This commit is contained in:
parent
405a2511af
commit
8c891a179a
17
sk1/Makefile
Normal file
17
sk1/Makefile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
CC=gcc
|
||||||
|
CFLAGS=-Wall -std=c99
|
||||||
|
TARGET=compressor
|
||||||
|
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): main.o compressor.o
|
||||||
|
$(CC) $(CFLAGS) -o $(TARGET) main.o compressor.o
|
||||||
|
|
||||||
|
main.o: main.c compressor.h
|
||||||
|
$(CC) $(CFLAGS) -c main.c
|
||||||
|
|
||||||
|
compressor.o: compressor.c compressor.h
|
||||||
|
$(CC) $(CFLAGS) -c compressor.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o $(TARGET)
|
||||||
65
sk1/README.md
Normal file
65
sk1/README.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
Zadanie
|
||||||
|
-Naprogramovať nástroj na kompresiu a dekompresiu binárnych súborov pomocou kompresného algoritmu (nie RLE). Program má podporovať súbory do 10 MB, dosiahnuť minimálne 10% kompresiu a po dekompresii má byť súbor identický s pôvodným.
|
||||||
|
|
||||||
|
Stručný opis funkčnosti
|
||||||
|
-Program komprimuje a dekomprimuje súbory pomocou Huffmanovho kódovania. Pri
|
||||||
|
kompresii analyzuje frekvenciu výskytu jednotlivých bajtov a vytvorí pre ne
|
||||||
|
optimálne binárne kódy - častejšie bajty dostanú kratšie kódy. Pri dekompresii
|
||||||
|
použije uloženú frekvenčnú tabuľku na rekonštrukciu pôvodných dát.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
STRUČNY OPIS RIEŠENIA
|
||||||
|
|
||||||
|
|
||||||
|
Program používa Huffmanovo kódovanie:
|
||||||
|
|
||||||
|
Kompresia:
|
||||||
|
Načíta súbor do pamäte
|
||||||
|
Spočíta frekvenciu každého bajtu (0-255)
|
||||||
|
Vytvorí zoznam uzlov pre bajty s nenulovou frekvenciou
|
||||||
|
Zoradí uzly podľa frekvencie (bubble sort)
|
||||||
|
Postupne spája dva uzly s najnižšou frekvenciou do stromu
|
||||||
|
Vygeneruje binárne kódy prechádzaním stromu
|
||||||
|
Zakóduje dáta pomocou týchto kódov bit po bite
|
||||||
|
Uloží veľkosť, frekvenčnú tabuľku a komprimované dáta
|
||||||
|
|
||||||
|
|
||||||
|
Dekompresia:
|
||||||
|
Načíta veľkosť a frekvenčnú tabuľku
|
||||||
|
Zrekonštruuje Huffmanov strom rovnakým spôsobom
|
||||||
|
Číta komprimované dáta bit po bite
|
||||||
|
Pre každý bit prechádza stromom (0=vľavo, 1=vpravo)
|
||||||
|
Keď dosiahne list, zapíše zodpovedajúci bajt
|
||||||
|
Pokračuje kým nedekóduje celý súbor
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
PODMIENKY FUNGOVANIA
|
||||||
|
|
||||||
|
|
||||||
|
Program funguje správne za týchto podmienok:
|
||||||
|
|
||||||
|
Vstupný súbor existuje a je čitateľný
|
||||||
|
Veľkosť súboru je maximálne 10 MB
|
||||||
|
Dostatočná voľná pamäť (aspoň 2× veľkosť súboru)
|
||||||
|
Súbor nie je prázdny
|
||||||
|
Pri dekompresii sa používa súbor vytvorený týmto programom
|
||||||
|
|
||||||
|
Kompresia:
|
||||||
|
|
||||||
|
Textové súbory: 40-60% kompresia
|
||||||
|
Zdrojové kódy: 30-50% kompresia
|
||||||
|
Binárne súbory: 5-30% kompresia (závisí od obsahu)
|
||||||
|
Už komprimované súbory (zip, jpg): minimálna alebo žiadna kompresia
|
||||||
|
Veľmi malé súbory (<1 KB): môžu byť väčšie kvôli hlavičke (1032 B)
|
||||||
|
|
||||||
|
|
||||||
|
ZOZNAM POUŽITYCH ZDROJOV
|
||||||
|
|
||||||
|
Youtube: https://www.youtube.com/watch?v=JsTptu56GM8
|
||||||
|
Youtube: https://www.youtube.com/watch?v=iiGZ947Tcck
|
||||||
|
Youtube: https://www.youtube.com/watch?v=goOa3DGezUA
|
||||||
|
Internet: https://en.wikipedia.org/wiki/LZ77_and_LZ78
|
||||||
|
Internet: ChatGBT Prompt: Príklady kompresie dát
|
||||||
|
|
||||||
263
sk1/compressor.c
Normal file
263
sk1/compressor.c
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "compressor.h"
|
||||||
|
|
||||||
|
struct node {
|
||||||
|
unsigned char znak;
|
||||||
|
int pocet;
|
||||||
|
struct node *left;
|
||||||
|
struct node *right;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct node* vytvor_node(unsigned char c, int p) {
|
||||||
|
struct node* n = malloc(sizeof(struct node));
|
||||||
|
n->znak = c;
|
||||||
|
n->pocet = p;
|
||||||
|
n->left = NULL;
|
||||||
|
n->right = NULL;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int compress_file(char *input, char *output) {
|
||||||
|
FILE *f = fopen(input, "rb");
|
||||||
|
if(!f) {
|
||||||
|
printf("Neviem otvorit subor\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long velkost = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
|
unsigned char *data = malloc(velkost);
|
||||||
|
fread(data, 1, velkost, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
int frekvencia[256];
|
||||||
|
int i;
|
||||||
|
for(i=0;i<256;i++) frekvencia[i]=0;
|
||||||
|
for(i=0;i<velkost;i++) frekvencia[data[i]]++;
|
||||||
|
|
||||||
|
struct node* pole[256];
|
||||||
|
int n=0;
|
||||||
|
for(i=0;i<256;i++){
|
||||||
|
if(frekvencia[i]>0){
|
||||||
|
pole[n] = vytvor_node(i, frekvencia[i]);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0;i<n-1;i++){
|
||||||
|
for(int j=0;j<n-i-1;j++){
|
||||||
|
if(pole[j]->pocet > pole[j+1]->pocet){
|
||||||
|
struct node* tmp = pole[j];
|
||||||
|
pole[j] = pole[j+1];
|
||||||
|
pole[j+1] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while(n>1){
|
||||||
|
struct node* left = pole[0];
|
||||||
|
struct node* right = pole[1];
|
||||||
|
|
||||||
|
struct node* parent = vytvor_node(0, left->pocet + right->pocet);
|
||||||
|
parent->left = left;
|
||||||
|
parent->right = right;
|
||||||
|
|
||||||
|
for(i=0;i<n-2;i++){
|
||||||
|
pole[i] = pole[i+2];
|
||||||
|
}
|
||||||
|
n = n-2;
|
||||||
|
|
||||||
|
int vlozene=0;
|
||||||
|
for(i=0;i<n;i++){
|
||||||
|
if(parent->pocet < pole[i]->pocet){
|
||||||
|
for(int j=n;j>i;j--){
|
||||||
|
pole[j] = pole[j-1];
|
||||||
|
}
|
||||||
|
pole[i] = parent;
|
||||||
|
vlozene=1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!vlozene) pole[n] = parent;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* root = pole[0];
|
||||||
|
|
||||||
|
unsigned int kody[256];
|
||||||
|
unsigned char dlzky[256];
|
||||||
|
for(i=0;i<256;i++){
|
||||||
|
kody[i]=0;
|
||||||
|
dlzky[i]=0;
|
||||||
|
}
|
||||||
|
struct node* zasobnik[512];
|
||||||
|
unsigned int kod_zasobnik[512];
|
||||||
|
unsigned char dlzka_zasobnik[512];
|
||||||
|
int top=0;
|
||||||
|
|
||||||
|
zasobnik[top] = root;
|
||||||
|
kod_zasobnik[top] = 0;
|
||||||
|
dlzka_zasobnik[top] = 0;
|
||||||
|
top++;
|
||||||
|
|
||||||
|
while(top>0){
|
||||||
|
top--;
|
||||||
|
struct node* aktualny = zasobnik[top];
|
||||||
|
unsigned int kod = kod_zasobnik[top];
|
||||||
|
unsigned char dlzka = dlzka_zasobnik[top];
|
||||||
|
|
||||||
|
if(!aktualny->left && !aktualny->right){
|
||||||
|
kody[aktualny->znak] = kod;
|
||||||
|
dlzky[aktualny->znak] = dlzka>0 ? dlzka : 1;
|
||||||
|
}else{
|
||||||
|
if(aktualny->left){
|
||||||
|
zasobnik[top] = aktualny->left;
|
||||||
|
kod_zasobnik[top] = kod<<1;
|
||||||
|
dlzka_zasobnik[top] = dlzka+1;
|
||||||
|
top++;
|
||||||
|
}
|
||||||
|
if(aktualny->right){
|
||||||
|
zasobnik[top] = aktualny->right;
|
||||||
|
kod_zasobnik[top] = (kod<<1)|1;
|
||||||
|
dlzka_zasobnik[top] = dlzka+1;
|
||||||
|
top++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsigned char* vystup = malloc(velkost*2);
|
||||||
|
long pos=0;
|
||||||
|
unsigned char bajt=0;
|
||||||
|
int bit_pos=0;
|
||||||
|
|
||||||
|
for(i=0;i<velkost;i++){
|
||||||
|
unsigned int kod = kody[data[i]];
|
||||||
|
unsigned char dlzka = dlzky[data[i]];
|
||||||
|
|
||||||
|
for(int j=dlzka-1;j>=0;j--){
|
||||||
|
if((kod>>j)&1){
|
||||||
|
bajt |= (1<<(7-bit_pos));
|
||||||
|
}
|
||||||
|
bit_pos++;
|
||||||
|
if(bit_pos==8){
|
||||||
|
vystup[pos++] = bajt;
|
||||||
|
bajt=0;
|
||||||
|
bit_pos=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(bit_pos>0) vystup[pos++]=bajt;
|
||||||
|
|
||||||
|
FILE *fout = fopen(output, "wb");
|
||||||
|
fwrite(&velkost, sizeof(long), 1, fout);
|
||||||
|
fwrite(frekvencia, sizeof(int), 256, fout);
|
||||||
|
fwrite(vystup, 1, pos, fout);
|
||||||
|
fclose(fout);
|
||||||
|
|
||||||
|
printf("Komprimovane: %ld -> %ld\n", velkost, pos+sizeof(long)+256*sizeof(int));
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
free(vystup);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int decompress_file(char *input, char *output) {
|
||||||
|
FILE *f = fopen(input, "rb");
|
||||||
|
if(!f){
|
||||||
|
printf("Neviem otvorit subor\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
long velkost;
|
||||||
|
int frekvencia[256];
|
||||||
|
fread(&velkost, sizeof(long), 1, f);
|
||||||
|
fread(frekvencia, sizeof(int), 256, f);
|
||||||
|
|
||||||
|
struct node* pole[256];
|
||||||
|
int n=0;
|
||||||
|
int i;
|
||||||
|
for(i=0;i<256;i++){
|
||||||
|
if(frekvencia[i]>0){
|
||||||
|
pole[n] = vytvor_node(i, frekvencia[i]);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i=0;i<n-1;i++){
|
||||||
|
for(int j=0;j<n-i-1;j++){
|
||||||
|
if(pole[j]->pocet > pole[j+1]->pocet){
|
||||||
|
struct node* tmp = pole[j];
|
||||||
|
pole[j] = pole[j+1];
|
||||||
|
pole[j+1] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while(n>1){
|
||||||
|
struct node* left = pole[0];
|
||||||
|
struct node* right = pole[1];
|
||||||
|
|
||||||
|
struct node* parent = vytvor_node(0, left->pocet + right->pocet);
|
||||||
|
parent->left = left;
|
||||||
|
parent->right = right;
|
||||||
|
|
||||||
|
for(i=0;i<n-2;i++){
|
||||||
|
pole[i] = pole[i+2];
|
||||||
|
}
|
||||||
|
n = n-2;
|
||||||
|
|
||||||
|
int vlozene=0;
|
||||||
|
for(i=0;i<n;i++){
|
||||||
|
if(parent->pocet < pole[i]->pocet){
|
||||||
|
for(int j=n;j>i;j--){
|
||||||
|
pole[j] = pole[j-1];
|
||||||
|
}
|
||||||
|
pole[i] = parent;
|
||||||
|
vlozene=1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!vlozene) pole[n] = parent;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* root = pole[0];
|
||||||
|
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long total = ftell(f);
|
||||||
|
long komp_velkost = total - sizeof(long) - 256*sizeof(int);
|
||||||
|
fseek(f, sizeof(long) + 256*sizeof(int), SEEK_SET);
|
||||||
|
|
||||||
|
unsigned char *komp_data = malloc(komp_velkost);
|
||||||
|
fread(komp_data, 1, komp_velkost, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
unsigned char *vystup = malloc(velkost);
|
||||||
|
long vystup_pos=0;
|
||||||
|
struct node* aktualny = root;
|
||||||
|
|
||||||
|
for(i=0;i<komp_velkost && vystup_pos<velkost;i++){
|
||||||
|
for(int bit=7;bit>=0 && vystup_pos<velkost;bit--){
|
||||||
|
if((komp_data[i]>>bit)&1){
|
||||||
|
aktualny = aktualny->right;
|
||||||
|
}else{
|
||||||
|
aktualny = aktualny->left;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!aktualny->left && !aktualny->right){
|
||||||
|
vystup[vystup_pos++] = aktualny->znak;
|
||||||
|
aktualny = root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fout = fopen(output, "wb");
|
||||||
|
fwrite(vystup, 1, velkost, fout);
|
||||||
|
fclose(fout);
|
||||||
|
|
||||||
|
printf("Dekomprimovane: %ld\n", velkost);
|
||||||
|
|
||||||
|
free(komp_data);
|
||||||
|
free(vystup);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
7
sk1/compressor.h
Normal file
7
sk1/compressor.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#ifndef COMPRESSOR_H
|
||||||
|
#define COMPRESSOR_H
|
||||||
|
|
||||||
|
int compress_file(char *input, char *output);
|
||||||
|
int decompress_file(char *input, char *output);
|
||||||
|
|
||||||
|
#endif
|
||||||
36
sk1/main.c
Normal file
36
sk1/main.c
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "compressor.h"
|
||||||
|
|
||||||
|
void pomoc(){
|
||||||
|
printf("Huffman kompresor\n");
|
||||||
|
printf("Pouzitie:\n");
|
||||||
|
printf(" ./compressor -c <vstupny_subor> <vystupny_subor>\n");
|
||||||
|
printf(" ./compressor -d <komprimovany_subor> <vystupny_subor>\n");
|
||||||
|
printf(" ./compressor -h\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]){
|
||||||
|
if(argc==2 && strcmp(argv[1], "-h")==0){
|
||||||
|
pomoc();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(argc!=4) {
|
||||||
|
printf("Nespravny pocet argumentov!\n");
|
||||||
|
pomoc();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strcmp(argv[1], "-c")==0){
|
||||||
|
return compress_file(argv[2], argv[3]);
|
||||||
|
}else if(strcmp(argv[1], "-d")==0) {
|
||||||
|
return decompress_file(argv[2], argv[3]);
|
||||||
|
}else{
|
||||||
|
printf("Neznamy prepinac: %s\n", argv[1]);
|
||||||
|
pomoc();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user