본문 바로가기
개발

[알고리즘]허프만 코드 알고리즘을 이용한 압축 프로그램 소스코드(3/3) - DeCompressor.cpp

by 비손 2008. 12. 16.
압축을 해제하는 코드입니다. 이 코드에서도 'huffman.h'를 이용해도 되지만, 별로 많은 소스를 필요로 하지 않아서 그냥 자체 제작했습니다.
해제 코드는 압축 코드보다 훨씬 더 간단합니다.
압축할 때 생겨난 '파일명.huf'파일과 '파일명.cmp'파일이 있어야 하며, 해제할 파일명 입력할 때 '파일명.cmp'를 입력합니다. 출력 파일은 '파일명.cmp.ucp'입니다.
// DeCompressor.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//

#include "stdafx.h"
#include <stdlib.h>
#include <string.h>

///////////////////////////////////////////////허프만 트리 관련 ////////////////////////////////////

//허프만 트리 노드
typedef struct huffNode
{
	short value; //값
	int freq; //빈도
	struct huffNode* left; //왼쪽 노드
	struct huffNode* right; //오른쪽 노드
}huffNode;

//파일을 읽어가면서 트리를 만듭니다.
void tree_loadHuff(huffNode* head, FILE* hufFile)
{
	//입력할 데이터를 담을 변수
	int data;

	//파일을 읽으면서 트리를 만들어갑니다.
	while(fscanf(hufFile,"%d:",&data) != EOF)
	{
		//node를 head로 돌립니다.
		huffNode* node = head;

		while(1)
		{
			int i = fgetc(hufFile);

			//한 줄 종료
			if(i == '\n')
			{
				//현재 위치한 노드에 값을 넣고 종료합니다.
				node->value = data;
				break;
			}
			//숫자가 나온 경우 해당 노드로 이동합니다.
			else
			{
				//왼쪽 노드인 경우
				if(i == 48) //아스키코드에서 '0'이 48이므로, 바이너리로 읽지 않기 때문에 아스키 코드를 사용
				{	
					//왼쪽 노드가 없는 경우, 왼쪽 노드를 생성합니다.
					if(node->left == NULL)
					{
						node->left = (huffNode*)malloc(sizeof(huffNode));
						node->left->left = NULL;
						node->left->right = NULL;
					}
					node = node->left;
				}
				//오른쪽 노드인 경우
				else
				{
					//오른쪽 노드가 없는 경우, 오른쪽 노드를 생성합니다.
					if(node->right == NULL)
					{
						node->right = (huffNode*)malloc(sizeof(huffNode));
						node->right->left = NULL;
						node->right->right = NULL;
					}
					node = node->right;
				}
			}
		}
	}
}

////////////////////////////////////허프만 트리 관련 끝///////////////////////////////////////////

////////////////////////////////////////////////// bit 처리 //////////////////////////////////////////////////////////

//기록하고 남은 데이터가 저장되는 공간
unsigned int wbData = 0;
//남은 데이터가 몇 비트인지 저장하는 변수
int remBit = 0;

//허프만 데이터를 기준으로 파일에 기록합니다.
//head: 허프만자료, in: 입력 파일 , out: 출력할 파일
void writeData(huffNode* head, FILE* in, FILE* out)
{
	//char 변수
	//unsigned를 하지 않으면 맨 처음 비트가 1이 될 경우 음수로 계산하므로 unsigned로 지정해 주어야 한다.
	unsigned char ch[1]; 
	//node에 head를 저장합니다.
	huffNode* node = head;

	while(fread(ch,sizeof(char),1,in))
	{
		//기존 자료와 합칩니다.
		wbData = (wbData << 8) + ch[0];
		remBit += 8;

		//node의 값을 다시 설정합니다.
		node = head;

		//남은 비트가 1개 이상일 때 작동합니다.
		for(int i=remBit-1; i >= 0; i--)
		{
			//노드를 비트에 맞춰 이동시킵니다.
			if(((wbData >> i) & 1) == 0)
				node = node->left;
			else
				node = node->right;

			if(node->left == NULL) //현재 노드가 Leaf인 경우
			{
				//종결 문자가 나온 경우 함수를 종료합니다.
				if(node->value == 256)
					return;

				//버퍼
				char temp[1] = {node->value};
				//파일에 기록합니다.
				fwrite(temp,sizeof(char),1,out);
				//남은 비트수에 i값을 넣습니다.
				remBit = i;

				//node의 값을 다시 설정합니다.
				node = head;
			}
		}
	}
}

////////////////////////////////////////////////// bit 처리 끝////////////////////////////////////////////////////////

int _tmain(int argc, _TCHAR* argv[])
{
	char fileName[100];
	FILE *inputFile; //소스파일(압축)
	FILE *outputFile; //목적파일(압축 해제된 파일)
	FILE *hufFile; //허프만 데이터 파일

	//파일 변수 정의하기
	printf("압축을 해제할 파일 이름을 입력하세요(예:abc.txt.cmp):");
	scanf("%s",&fileName);
	inputFile = fopen(fileName,"rb");
	//입력파일 존재 여부 확인
	if(inputFile == NULL)
	{
		printf("파일을 열 수 없습니다. 프로그램을 종료합니다.\n");
		system("pause");
		return -1;
	}
	outputFile = fopen(strcat(fileName,".ucp"),"wb");
	//hufFile명 생성
	int pos = 0;
	while(fileName[pos] != '\0')
		pos++;
	fileName[pos-7] = 'h';
	fileName[pos-6] = 'u';
	fileName[pos-5] = 'f';
	fileName[pos-4] = '\0';
	hufFile = fopen(fileName,"r");
	//출력파일 사용 가능 여부 확인
	if(outputFile == NULL || hufFile == NULL)
	{
		printf("파일을 열 수 없습니다. 프로그램을 종료합니다.\n");
		system("pause");
		return -1;
	}

	//트리를 생성할 노드입니다.
	huffNode* huff = (huffNode*)malloc(sizeof(huffNode));
	huff->right = NULL;
	huff->left = NULL;
	//트리를 생성합니다.
	tree_loadHuff(huff,hufFile);

	//생성된 트리를 바탕으로 원본 파일을 생성해 냅니다.
	writeData(huff,inputFile,outputFile);

	//메세지 출력
	printf("압축 해제를 완료했습니다.\n");
	system("pause");
	return 0;
}