티스토리 뷰

학부 저학년 때 개발했던 프로그램입니다. 그때는 성능보다는 개발을 우선으로 해서, 과제용으로는 충분할지 모르겠으나 어디 보여주기엔 좀 그래서... 외장하드에 묻어두다가 그래도 그때 개발했던 내용을 공유하고자 글을 씁니다. 그리고 그때 부족하다고 생각했던 부분들을 수정해서 성능도 훨씬 끌어 올렸습니다. 처음엔 C로 구현을 했으나, heap 같은 라이브러리를 사용하기 위해 C++로 구현했습니다. ( C에서 직접 구현해서 사용해도 괜찮습니다)


0. 주저리주저리

허프만코드를 사용해서 압축을 한 프로그램입니다. 결국엔 파일을 읽어서 빈도수를 구한 뒤 허프만 코드를 만들고, 1byte에 1bit씩 채워나가면서 프로그램을 압축하는 형식입니다. 

프로그램은 압축, 압축해제 2개의 큰 부분으로 나뉩니다.

 

압축은 허프만트리를 생성해서 코드를 구합니다. 이 코드가 압축과 압축해제의 연결고리가 됩니다. 압축할 때 생성한 코드의 규칙에 맞게 데이터를 인코딩합니다. 압축 해제할 때 프로그램이 이 '코드'를 가지고 있어야 해독을 할 수 있기 때문에 압축파일의 헤더 부분에 서로 약속한 규격으로 코드를 적어두어야 합니다.

 

압축해제는 코드를 가지고 1bit씩 읽어 buffer에 쌓습니다. 1bit씩 쌓일 때마다 갖고 있는 해독 코드표를 보면서 일치하는 게 있나.. 검사하며 있으면 그 코드에 해당하는 글자로 바꿔서 압축해제 파일에 쓰는 방식입니다.

 

프로그램의 대부분의 연산이 어디서 발생하는지 아시겠나요?

분석은 밑에서 하겠습니다.

 

1. 허프만 코딩이란?

ko.wikipedia.org/wiki/%ED%97%88%ED%94%84%EB%A8%BC_%EB%B6%80%ED%98%B8%ED%99%94

 

허프먼 부호화 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전.

ko.wikipedia.org

이 자료에 따르면, 허프만 부호화(Huffman coding)은 무손실 압축에 쓰이는 엔트로피 부호화의 일종이라고 합니다.

주어진 문장의 빈도수를 세어 빈도수가 많은문자는 짧은 길이의 코드로 대체,

빈도수가 적은 문자는 긴 길이의 코드로 대체하는 방식입니다.

 

Hello C world!로 예를 들어보겠습니다.

 

우선 빈도수를 세어 오름차순으로 정렬합니다.

 

그림 0. 초기상태 (문자, 빈도수)

위처럼, 문자와 빈도수로 나타내자. 빈도수 기준으로 오름차순으로 정렬.

 

이제 가장 왼쪽(빈도수 적은) 노드 2개를 합칠 것이다. 그리고 다시 빈도수 기준으로 오름차순으로 정렬.

 

그림 1. 합치고 다시 정렬한 상태

그럼 (1)과 같은 상태가 된다. 이런 것을 계속 반복하다 보면,

 

그림 2. 완성된 트리

이런 트리가 하나 만들어집니다.

★정렬할 때 삽입 순서를 어떻게 하냐 결과가 달라질 수는 있지만, 코드 길이 * 문자의 빈도수를 모두 합한 값은 항상 똑같이 나옵니다.

 

여기서 규칙은 마음대로 정해도 되는데, 왼쪽 자식으로 내려갈 때 0, 오른쪽으로 내려갈때 1이라는 규칙을 적용하면 밑의 그림과 같이 나타낼 수 있습니다.

 

그림 3. 코드 부여

루트에서 길을 계속 따라내려 가보면,

H : 000

e : 001

o : 01

I : 10

r : 1100

d : 1101

C : 1110

w : 1111

로 나타낼 수 있습니다.

 

자 그러면, 이런 아이디어를 가지고 압축하는 과정을 들여다보겠습니다.

특별한 내용은 없습니다. 위의 그림 0~3까지의 과정을 코드로 옮기기만 하면 됩니다.

 

2. 압축 과정 (인코딩)

압축과정은 위의 1번에서 설명드렸던 코드를 만드는 것입니다.

 

2.1) 빈도수 측정

파일을 열어서 처음부터 끝까지 읽어야 합니다. ASCII 코드 같은 경우는 1byte로 해결이 되지만 한글 같은 경우는 2byte라서 특별한 처리를 해주어야 합니다.

 

ANSI기준으로,

ASCII 코드는 1byte이고 0~127의 값을 가지고 있기 때문에 [128]의 배열로 선언해줍니다.

 

한글은 조금 달라서 알아볼 필요가 있습니다.

한글은 2byte입니다.

상위 1byte는 0xB0 ~ 0xC8 (총 25자)

하위 1byte는 0xA1 ~ 0xFE (총 94자)

= 2350자

를 나타냅니다.

그래서 배열을 [25][94]자로 확인을 해야 합니다.

 

그래서 어떠한 문자를 읽었을 때, 그 값이 ASCII code값보다 크다면, (Read Byte > 127) 한글이라고 판단할 수 있습니다. 혹은 읽은 문자 값이 ASCII code 범위라면 영어 혹은 특수문자(. , : " ' 등)이라고 판단할 수 있는 것입니다.

 

그래서 빈도수를 읽는 코드를 살펴보면

bool cal_frequency(string filename, int  freq[][94], int *freqascii)
{
	//ANSI
	FILE * file = fopen(filename.c_str(), "rt");

	if (file == NULL) {
		//printf("File open error!\n");
		return ERROR;
	}

	unsigned char index[2] = { 0 };

	while (fscanf(file, "%c", &index[0]) != EOF)
	{
		if (index[0] > 127)	//korean
		{
			fscanf(file, "%c", &index[1]);
			++freq[index[0] - 0xB0][index[1] - 0xA1];
		}
		else// ASCII
		{
			++freqascii[index[0]];
		}

	}

	fclose(file);
	return true;	//no error
}

 

파일을 읽어서 EOF를 만날 때까지 한글이면 한글 배열에, ASCII코드라면 ASCII코드 배열에 1을 증가시켜줍니다.

 

 

2.2. 노드 만들기

이제 빈도수를 측정했으니, 그림 0번과 같은 문자, 빈도수를 갖고 있는 노드들을 만들어 줄 것이다.

 

그래서 여기서는 tree라는 클래스(구조체도 ok, 오히려 구조체가 개념에 맞을 듯싶습니다.)를 만들었다.

트리를 만들 계획이기 때문에 tree로 네이밍을 했고,

자식들을 연결하는 것은 포인터로 원래 구현을 하나 new 랑 delete 하기 귀찮아서 그냥 STL의 List를 사용했습니다.

 

class tree
{
public :
	tree(){}
	tree(unsigned char name1_, unsigned char name2_, int freq_) { name[0] = name1_; name[1] = name2_;  freq = freq_;  left_child = list<tree>(); right_child = list<tree>(); }
	//data

	unsigned char name[2];	//한글 2bytes
	int freq;	//frequency

	//child
	list<tree>  left_child, right_child;
};

 

문자를 담을 2개의 char배열,

만약 아스키코드라면 [0] 번째 배열만 사용할 것입니다.

그리고 빈도수를 담고 있는 int변수,

자식들을 연결할 list 2개입니다.

 

그래서 이 클래스를 활용해서 우선순위 큐에 집어넣을 겁니다. 우선순위 큐는 빈도수를 기준으로 오름차순으로 정렬하도록 정의했습니다.

(C++문법 관련해서는 밑의 링크를 참조하시거나, 이 코드 링크를 밑에 걸어두었는데 거기서 확인하시길 바랍니다^^)

kibbomi.tistory.com/149

 

[C++ / STL] Queue, Priority Queue 분석

BFS에 자주 사용되는 Queue (알고리즘 문제풀이 기준), 그리고 정렬에 자주 사용되는 Prioriry Queue(우선순위 큐, 힙)의 사용법에 대해서 알아보도록 하겠습니다. ( Queue에 대한 정보는 C++ Reference를 참

kibbomi.tistory.com

 

void make_node(int freq[][94], int *freqascii, priority_queue<tree, vector<tree>, Mycomp_tree> &pq)
{
	//한글처리
	for (int i = 0; i < 25; i++)
		for (int j = 0; j < 94; j++)
			if (freq[i][j] != 0)	//존재하는 글자만
			{
				tree item(i + 0xB0, j + 0xA1, freq[i][j]);
				pq.push(item);
			}
	
	//아스키코드 처리
	for (unsigned char i = 0; i < 128; i++)
		if (freqascii[i] != 0)	//존재하는 글자만 추출	
		{
			tree item(i, 0, freqascii[i]);
			pq.push(item);
		}

	return;
}

위의 코드는 빈도수 배열들을 받아서 우선순위 큐(Priority Queue, 여기서는 pq)에 담습니다.

빈도수 별로 상위 2개의 노드를 합치기 때문에 우선순위 큐에 담고 있습니다. 힙 정렬이기 때문에 삽입은 O(logn)입니다.

 

한글 배열, 아스키코드 배열을 모두 순회하면서 빈도수가 1이라도 있는 것들을 생성하고 pq에 삽입해주었습니다.

 

 

 

2.3. 트리 만들기

이제 그림 1의 과정을 거쳐 그림 2를 만드는 것을 하는 함수를 만들 것입니다.

 

이전 2.2단계에서 만들었던 pq를 입력으로 받아서 2개씩 끄집어낸 뒤 합치고 다시 삽입하는 그런 과정입니다.

 

결국 pq는 자동으로 정렬을 해주니까, 상위 2개 item을 꺼내서 합치고 다시 넣어주는 과정만 있으면 됩니다.

 

void make_tree(priority_queue<tree, vector<tree>, Mycomp_tree>  &pq)
{
	while (pq.size() >= 2)
	{
		tree left = pq.top();
		pq.pop();
		tree right = pq.top();
		pq.pop();

		tree parent;
		parent.left_child.push_back(left);
		parent.right_child.push_back(right);
		parent.freq = left.freq + right.freq;

		pq.push(parent);
	}
	return;
}

 

left, right에 1개씩 할당하고,

합칠 parent 객체를 만들고 child에 연결한 뒤, 빈도수를 더하고 다시 pq에 넣어줍니다.

 

2.4. 코드 부여하기

이제는 저 빈도수 tree를 사용해서 code tree를 만들어야 합니다.

bool make_code(const priority_queue<tree, vector<tree>, Mycomp_tree>& pq, priority_queue<code, vector<code>, Mycomp_code> &huffcode)
{

	inorder(pq.top(), huffcode,"");

	return true;
}

코드를 만드는 함수는 다음과 같이 정의했습니다.

pq의 내용을 활용해서 huffcode pq의 내용을 채울 것입니다.

 

여기서 새로운 code라는 클래스(구조체)가 등장합니다.

class code
{
public:

	//data
	unsigned char name[2];
	string huffcode;	//허프만부호화코드가 담길곳
};

이 code구조체는 압축과 압축해제 사이의 중요한 역할을 합니다. 이 code가 Key 같은 느낌입니다. (설명서 같은 느낌)

code에는 해당 문자정보와, 어떠한 code로 변경될지 그 정보를 담는 string변수가 1개 있습니다.

 

tree를 순회하는 방식에는 3가지가 있는데 preorder, inorder, postorder, 순서대로 전위, 중위, 후위 순회입니다. 여기서는 중위 순회를 선택했습니다.

void inorder(const tree & root, priority_queue<code, vector<code>, Mycomp_code> &huffcode, string cur_code)
{

	if(!root.left_child.empty())
		inorder(root.left_child.front(), huffcode, cur_code + "0");
	
	//is leaf,
	if (root.left_child.empty() && root.right_child.empty()) {
		code item;
		
		if (root.name[0] > 127)	//korean
		{
			item.name[0] = root.name[0];
			item.name[1] = root.name[1];
			item.huffcode = cur_code;
			huffcode.push(item);
		}
		else
		{
			item.name[0] = root.name[0];
			item.name[1] = 0;
			item.huffcode = cur_code;
			huffcode.push(item);
		}
	}

	if (!root.right_child.empty())
	inorder(root.right_child.front(), huffcode, cur_code + "1");

	return;
}

루트의 정보와, huffcode의 정보, 현재 code의 정보를 입력으로 받습니다.

재귀함수에 익숙하지 않으신 분들은 이게 어려울 수 있는데, 보통 재귀함수는 큰 문제를 작은 문제로 쪼갤 때 활용할 수 있습니다. 여기서는 트리의 자식을 잡아서 들어도 트리이기 때문에 재귀함수를 적용할 수 있습니다.(같은 문제)

 

왼쪽으로 내려갈 때는 0을 붙이고 오른쪽으로 내려갈때는 1을 붙여주면서 3번째 매개변수에 코드를 전달했습니다.

중위 순회이기 때문에 값을 대입하는 게 이동하는 것 사이에 존재하게 되고, 순회한다는 개념은 값을 복사하고 저장한다는 의미이고 따라서 code객채를 만들어주고 huffcode pq에 삽입해주었습니다. huffcode는 코드를 사전적 순서로 정렬했습니다.

 

2.5. 압축하기

이제 드디어 압축을 할 수 있습니다.

압축의 원리는 String변수에 담아놨던 코드들을 1byte 안에 꾸깃꾸깃 넣으면 됩니다.

지금 string변수 (code)에 "01011110"이 만약 있다면 8byte로 나타내고 있는 것입니다. 그런데 이것을  1byte의 8bit에 넣자 라는 것 입니다. 그래서 해당 1byte는 01011110에 해당하는 어떠한 값을 출력하게 됩니다. 그래서 압축파일을 열어보면 이상한 글자가 많습니다.

 

아이디어는 위와 같고 이제 압축을 할 차례입니다.

압축을 하려면 빈도수 계산했던 것처럼 파일을 처음부터 끝까지 읽어가면서 huffcode pq를 참조해가면서 코드로 바꾸면 됩니다.  결국 huffcode pq가 Key역할을 하는 것이고 이 정보를 압축파일의 헤더에 저장해놓아서 압축 해제할 때 사용해야만 합니다. 그래서 압축파일의 구조는 (huffcode의 정보 & 그 외 정보) + (압축 data)가 들어가게 됩니다.

 

헤더는 설계하기 나름입니다만, 저 같은 경우는,

마지막 byte에는 의미 있는 bit가 있고 의미가 없는 bit가 있을 것입니다.

그래서 파일의 마지막 byte에는 몇 개의 의미 있는 bit가 존재하는지 나타내는 변수 1개,

huffcode 우선순위 큐의 원소 개수, 코드의 값(문자), code의 유효 bit 수, code data로 적었습니다.

위의 내용이 이해가 잘 안 될 텐데,

결국 huffcode를 만들어 내야 합니다. 처음은 이 pq를 만들어 내기 위해 몇 번 반복해야 할지 나타내는 수이고, 

huffcode가 지금 string으로 되어있기 때문에 1글자가 1byte입니다. 이것도 압축할 수 있으므로 1byte짜리 '0' or '1'을

1byte의 0___ ____ 으로 압축을 할 수 있습니다.

예를 들어 'a'라는 문자가 "00101"이라는 코드로 치환할 수 있을 때, 헤더에는

'a'는 그대로 기록되고, 유효 bit 수 5가 입력된 뒤, 0010 1000이렇게 1byte가 입력이 될 것입니다. 그래서 00101을 추출해내기 위해 5가 기록되는 것입니다.

 

이런 방식으로 헤더를 만듭니다.

bit를 채워 넣기 위해 shift연산을 사용했습니다.

구현은 제가 생각하는 간단한 방법으로 했기 때문에 (옛날 학부시절 코드 보면 토나옵니다) 코드를 보고 또 보시면 이해가 될 것입니다.

결국 데이터를 모두 읽고 문자에 해당하는 코드를 찾아 1byte 안에 넣는 과정이기 때문에.. 압축(인코딩)할 때는 1byte의 buffer라는 걸 사용해서 하면 편리합니다.

void convert_binary(string filename, priority_queue<code, vector<code>, Mycomp_code> & huffcode) 
{
	FILE *readfile = fopen(filename.c_str(), "rt");
	
	filename.erase(filename.length() - 4, 4);
	string savefilename = filename + ".bin";

	FILE *writefile = fopen(savefilename.c_str(), "wb");

	int dummy = 0;
	fprintf(writefile, "%c", dummy);
	//fprintf(writefile,"%c", (char)huffcode.size());
	fprintf(writefile, "%d", (int)huffcode.size());

	/*fclose(readfile);
	fclose(writefile);

	return;*/

	//본문 encoding용 복사.
	int idx = 0;
	vector<code> v(huffcode.size());

	while (!huffcode.empty())
	{
		code item = huffcode.top();
		huffcode.pop();
		v[idx++] = item;

		//korean
		if (item.name[0] > 127)
		{
			fprintf(writefile, "%c%c", item.name[0], item.name[1]);
		}
		//alphabet
		else
		{
			fprintf(writefile, "%c", item.name[0]);
		}

		
		fprintf(writefile, "%c",(char)item.huffcode.length());
		
		BYTE buffer = 0;
		int msb = -1;

		for (int i = 0; i < item.huffcode.length(); ++i)
		{
			if (msb == 7) {
				fprintf(writefile, "%c", buffer);
				buffer = 0;
				msb = -1;
			}

			buffer = buffer << 1;
			buffer = buffer | item.huffcode[i] - '0';
			++msb;
		}

		if (msb != -1) {
			while (msb != 7) {
				buffer = buffer << 1;
				msb++;
			}
			fprintf(writefile, "%c", buffer);
		}
	}
	//--Header encoding is finished--

	BYTE word[2];
	BYTE buffer = 0;
	int msb = -1;	//most significant bit

	while (fscanf(readfile, "%c", &word[0]) != EOF) {

		if (word[0] > 127)
			fscanf(readfile, "%c", &word[1]);

		//buffer의 글자에 해당하는 code를 획득
		string write_code = search_code(v, word);
		

		for (int i = 0; i < write_code.length(); ++i)
		{
			if (msb == 7) {
				fprintf(writefile, "%c", buffer);
				buffer = 0;
				msb = -1;
			}

			buffer = buffer << 1;
			buffer = buffer | write_code[i] - '0';
			++msb;

		}
	}
	//last byte
	int lastbit = msb;
	while (lastbit != 7) {
		buffer = buffer << 1;
		lastbit++;
	}

	fprintf(writefile, "%c", buffer);
	fseek(writefile, 0, SEEK_SET);
	fprintf(writefile, "%c", (char)msb);

	fclose(readfile);
	fclose(writefile);

	return;
}

가장 마지막 byte의 유효 bit 수는 그때까지 가지 않으면 모르기 때문에 압축을 마친 뒤 fseek함수를 통해서 찾아가서 다시 써주었습니다.

 

3. 압축 해제

다음은 압축해제입니다.

여기서 중요하게 생각할 점은 이전에 압축했을 때의 huffcode priority queue를 에러 없이 잘 복구하는 점,

그리고 여기서는 1byte를 읽을 때마다 코드를 참조하는 게 아니라 1bit를 읽을 때마다 pq를 찾아보며 똑같은 코드가 있는지 찾아봐야 하기 때문에 탐색 시간을 줄여야 하는 점 입니다. 탐색시간을 줄이기 위해서 이분 탐색 O(logn)을 사용했습니다.(이전 프로그램은 선형으로 탐색하도록 구현했었음. 지금 생각난 거지만 코드는 유일하기 때문에 트리를 한 칸씩 내려가면서 상태를 유지한다면 O(1) 시간에 탐색이 가능할 것 같은 생각이?? 아마도 구현 가능할 것 같은데 귀찮으니까 pass)

 

압축해제는 정말 간단합니다.

헤더를 써준 대로 그대로 읽어주기만 하면 되기 때문입니다.

물론 bit단위 연산이 많이 햇갈릴 수는 있지만, 그 부분은 코딩능력이기 때문에 어쩔 수 없습니다. 직접 하면서 실력을 늘리는 수밖에 없기 때문입니다.

 

이 부분은 특별한 알고리즘은 없습니다.

encoding 할 때의 역순으로 진행하면 됩니다.

1bit을 읽을 때마다 코드를 찾아야 한다는 점, 여기서는 code를 vector에 담아서 사용했습니다.

 

bool huffman_decode(string name)
{
	vector<code> v;	//만들어 내야함.

	FILE *file = fopen("test.bin", "rb");
	if (file == NULL)
	{
		return false;
	}
	FILE *decoded = fopen("decoded.txt", "wt");
	char msb;
	int codenum;

	fscanf(file, "%c", &msb);
	fscanf(file, "%d", &codenum);

	for (int i = 0; i < codenum; ++i)
	{
		code item;
		char validbit;

		fscanf(file,"%c", &item.name[0]);

		if (item.name[0] > 127)
			fscanf(file, "%c", &item.name[1]);
		else
			item.name[1] = 0;

		fscanf(file, "%c", &validbit);
		
		BYTE buffer = 0;
		while (validbit > 0)
		{
			fscanf(file, "%c", &buffer);

			for (int j = 7; j >= 0; --j){
				if (validbit <= 0)
					break;
				char bitdata = (buffer >> j) & 1;

				item.huffcode.push_back(bitdata + '0');
				--validbit;
			}
		}
		v.push_back(item);
	}

	/*for (code item : v)
	{
		printf("(%c%c, %s)\n", item.name[0],item.name[1], item.huffcode.c_str());
	}*/

	//header end.
	//decode start.

	//for binary search.
	sort(v.begin(), v.end(), MySort);

	BYTE buffer, EOFcheck;
	int cnt = 0;
	string huffcode;
	while (fscanf(file, "%c", &buffer) != EOF)
	{
		if (fscanf(file, "%c", &EOFcheck) == EOF)
		{
			for (int i = 7; i >= 7 - msb; --i)	//3번자리 까지..유효 즉 4개
			{
				char bitdata = (buffer >> i) & 1;
				huffcode.push_back(bitdata + '0');

				BYTE write_word[2] = { 0 };
				bool found = false;

				
				found = search_code(v, huffcode, write_word);
				
				if (found)
				{
					fprintf(decoded, "%c", write_word[0]);
					//printf("%c", write_word[0]);
					if (write_word[0] > 127) {
						fprintf(decoded, "%c", write_word[1]);
						//printf("%c", write_word[1]);
					}
					huffcode.clear();
					break;
				}
			}
		}
		else
		{
			fseek(file, -1, SEEK_CUR);
			for (int i = 7; i >= 0; --i)
			{
				char bitdata = (buffer >> i) & 1;
				huffcode.push_back(bitdata + '0');

				BYTE write_word[2] = { 0 };
				bool found = false;

				
				found = search_code(v, huffcode, write_word);
				
				if (found)
				{
					fprintf(decoded, "%c", write_word[0]);
					//printf("%c", write_word[0]);
					if (write_word[0] > 127) {
						fprintf(decoded, "%c", write_word[1]);
						//printf("%c", write_word[1]);
					}
					huffcode.clear();
				}
			}
		}
	}

	fclose(file);
	fclose(decoded);

	return true;
}

탐색을 위한 이분 탐색 코드는 밑과 같습니다.

bool search_code(vector<code> &v, string &s, BYTE word[2])
{
	//binary search for data structure 'code'
	int left = 0;
	int right = v.size() - 1;

	while (left <= right)
	{
		int mid = (left + right) / 2;

		if (v[mid].huffcode < s)
			left = mid + 1;

		else if (s < v[mid].huffcode)
			right = mid - 1;
		
		else{
			word[0] = v[mid].name[0];
			word[1] = v[mid].name[1];
			return true;
		}
	}
	return false;
}

 

 

4.  성능

이 부분을 넣은 이유는 알고리즘의 중요성을 부각하기 위함입니다.

이렇게 자료구조도 생각하고 탐색 시간을 고려한 프로그램과, 대충 배열에 때려 넣어서 찾거나 코드를 만든, 그런 프로그램의 시간차를 보여주고 싶습니다.

 

4.1) 개선 전 프로그램

예전 성능 개선 전 프로그램 경과 시간입니다.

 

실행시간

이게 x축 간격이 일정하지 않아서 곡선처럼 보이나 O(n)에 근접했습니다.

ASCII 시간
한글 시간
한글 ASCII 시간

 

4.2) 개선 후 프로그램

간단하게 1MB일 때만 보여드리겠습니다.

압축 관련하여서는 추가적으로 건든 것이 없기 때문에 압축률은 똑같을 것입니다.

 

 

개선 후 실행시간

압축 시간, 압축 해제 시간 모두 상당히 줄어든 것을 볼 수 있습니다

물론 여기서도 속도를 더 줄일 수 있는 방법이 있을 것입니다. 글을 쓰면서도 더 줄일 수 있는 방법이 떠 올랐습니다만.. 다시 개선하기도 귀찮고 하니,, 넘기는데

 

여튼 이렇게 효율적으로 구현하면 속도가 상당히 빨라지는 것을 좀 느끼셨으면 해서 작성했습니다

 

 

 

4.3) 실행 화면

실험 파일(1,002KB)

이렇게 아스키코드로 된 1,002KB(약1MB)의 Test.txt의 이름을 가진 텍스트 파일이 있다.

 

압축

이렇게 1번을 통해 압축을 선택하고

해당 파일의 이름을 입력하면 압축을 진행한다.

 

압축을하면 Test.txt -> Test.bin이란 파일이 생성되는데 이 경우 크기가 525KB로 줄었다.

이 파일을 열어보면

압축 파일.bin

당연히 알 수 없는 글자로 가득 차 있다.

 

이제 이걸 다시 압축 해제 하면

압축해제

다시 1,002KB의 Text.txt파일이 생성되고,

복원한 파일

위의 원본과 똑같은 파일이 생성된다.

 

 

5. 결론

허프만 코드를 사용해서 압축하는 프로그램을 만들어 보았습니다.

복잡한 연산이 있기 때문에 자료구조 선택에 큰 영향을 받았고 중요성을 깨달을 수 있었습니다.

 

그렇게 어렵지는 않았던 것 같습니다.

학부시절에는 몇 주간 프로젝트를 세워서 분석하고 설계하고 했는데 지금은 2일 정도 만에 개선하고 블로그에 글 써야지.. 만몇 주째 하다가 논문 발표가 끝나고 날 잡고 그냥 끝냈습니다.

 

코드는 밑에 첨부하겠습니다.

압축 프로그램이 큰 의미는 없지만 보시는 분께 도움이 된다면 그걸로 만족입니다!

 

(code)

github.com/Kibbomi/Huffman_Compression

 

지적 댓글 환영입니다~^^

댓글
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Total
Today
Yesterday