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