반응형
Zlib를 이용한 압축 프로그래밍
Posted on 2003/10/27
Topic: 응용라이브러리
article_zlib_prog위키 홈으로
원본 docbook문서 <META>

Zlib 활용

윤 상배

dreamyun@yahoo.co.kr

고친 과정
고침 0.8 2003년 10월 2일 23시
최초 문서작성

차례
1. 소개
2. Zlib를 이용한 압축 프로그래밍
2.1. 유틸리티 함수설명
2.1.1. compress
2.1.2. compress2
2.1.3. uncompress
2.1.4. gzopen
2.1.5. gzdopen
2.1.6. gzsetparams
2.1.7. gzread
2.1.8. gzwrite
2.1.9. int VAgzprintf
2.1.10. gzputs
2.1.11. gzgets
2.1.12. gzputc
2.1.13. gzgetc
2.1.14. gzfluseh
2.1.15. gzseek
2.1.16. gzrewind
2.1.17. gzeof
2.1.18. gzclose
2.1.19. gzerror
2.1.20. zlibVersion
2.2. 예제
2.2.1. 파일 압축 예제
2.2.2. 압축 해제 예제
2.3. 네트워크 애플리케이션에서의 활용

1. 소개

PC와 인터넷의 보급으로 데이터의 양이 급증하고 있다. 그러다 보니 데이터 저장공간에 많은 압박을 받게 된다. 거기에 더불어 인터넷이 대중화 되면서 데이터를 전송하기 위한 네트워크자원에의 압박도 받게 되었다.

이런 문제를 해결하기 위해서 개발된게 데이터 압축기술이며, Zlib는 범용적인 데이터의 압축을 위한 목적으로 개발되었다. 이 문서는 Zlib를 설명하기 위한 목적으로 작성되었으며, 레퍼런스 용도로써 유용하게 사용가능 할것이다. 또한 문서의 마지막에 간단한 활용예제를 둠으로써 좀더 쉽게 이해하도록 배려하고 있다.

이 문서의 많은 내용들은 Zlib레퍼런스 메뉴얼을 참고 하고 있다.


2. Zlib를 이용한 압축 프로그래밍

이번장에서는 Zlib에 대한 개략적인 소개와 함께 Zlib에서 제공하는 각종 함수에 대해서 다루게 된다. 그러나 모든 함수들에 대해서는 다루지 않고 프로그래밍 하는데 필요한 필수 함수들만을 다룰 것이다. 이외에도 zlib에서 제공되는 함수들이 있는데, 일반적인 응용 애플리케이션의 제작에는 거의 사용되지 않는 함수들이다. 이들 함수들에 대한 자세한 설명은 Zlib레퍼런스 메뉴얼를 참고하기 바란다.

지금은 단지 자주 사용되는 유틸리티 함수들만 설명하고 있는데, 시간이 된다면 zlib에서 제공하는 다른 함수들에 대해서도 설명하도록 하겠다. (zlib에서는 크게 유틸리티 함수, 기본 함수, 고급 함수 세개로 분류되어서 설명하고 있다.)


2.1. 유틸리티 함수설명

유틸리티 함수들은 말 그대로 응용 애플리케이션 레벨에서 간단하게 사용할 수 있는 높은 수준의 함수들로써 다음장에 설명하게될 기본함수들의 응용함수들이다.

높은 수준의 함수들인 만큼 사용하기 편하고 대부분의 압축작업을 하는데 있어서 여기에서 제공하는 것들로 충분할 것이다.

함수들을 살펴보면 알겠지만 파일관련 작업을 위해 사용되는 함수들과 이름이나 쓰임새가 매우 비슷하다는걸 알 수 있을 것이다. 기존의 표준적인 함수들과 비슷한 인터페이스를 유지하므로써 함수를 이해하고 사용하기가 좀더 수월하다.


2.1.1. compress

int compress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
				
이름에서 알 수 있듯이 데이터를 압축하기 위해서 사용된다. source데이터를 sourceLen크기 만큼 압축을 해서 dest 버퍼로 복사한다. destLen은 dest버퍼의 총 크기인데, 최소한 sourceLen 0.1%정도 크게 잡아 주어야 한다. 압축을 실시할 경우 반드시 데이터의 크기가 작아질 거라고 생각할 수 있겠지만 오히려 커지는 경우도 생길 수 있기 때문이다. 매우 작은 데이터를 압축하거나 이미 압축된 데이터를 압축하는 경우 압축된 데이터에 zlib헤더가 붙어서 오히려 데이터의 양이 더 커질 수도 있다.

compress는 성공적으로 압축되었을 경우 Z_OK를 메모리가 충분하지 않을경우 Z_MEM_ERROR, 버퍼의 크기가 충분하지 않을경우 Z_BUF_ERROR를 리턴한다.


2.1.2. compress2

int compress2(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level);
				
level이란 인자가 추가된것 외에는 compress와 완전히 동일하다. level은 압축의 정도를 결정하기 위해서 사용하는 것으로 0에서 9사이의 값을 가진다. 0은 가장 빠른 압축시간을 보여주며 9는 최고의 압축률을 보여준다. 만약 level을 0으로 하였다면 전혀 압축을 하지 않는다는 의미로 데이터 복사를 하는 것과 동일한 효과를 보여준다.

리턴값은 commpress와 완전히 동일함으로 참고하기 바란다.


2.1.3. uncompress

int uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen); 
				
압축된 데이터의 압축을 풀기 위해서 사용한다. 압축된 데이터 source를 sourceLen길이만큼 읽어서 압축을 해제한다음 dest버퍼에 저장한다. destLen는 dest버퍼의 크기로 압축이 풀릴 데이터의 크기를 예상해서 충분히 크게 잡아야 한다. 압축을 해제하는 작업이 성공적으로 이루어졌다면 실제 압축이 풀린 데이터의 크기가 destLen에 들어가게 된다.

성공적으로 수행되었을 경우 Z_OK가 리턴되며, 메모리가 충분치 않을경우 Z_MEM_ERROR, 버퍼의 크기가 충분치 않을 경우 Z_BUF_ERROR, 압축데이터가 잘못되어 있을 경우 Z_DATA_ERROR을 리턴한다.


2.1.4. gzopen

typedef voidp gzFile;

gzFile gzopen(const char *path, const char *mode);
				
압축된 데이터를 읽거나 쓰기 위해서 gzip파일을 연다. 두번째 인자인 mode는 "rb", "wb"등이 사용된다. 파일이 압축포멧된 파일이 아니더라도 상관은 없다. 성공적으로 파일을 열었을경우 gzFile를 리턴하는데, 압축파일 스트림으로 gzread, gzwrite등의 함수를 이용해서 실질적인 압축작업을 할때 (파일지정자)처럼 사용된다.

파일을 열기에 실패했을 경우 NULL을 리턴한다.


2.1.5. gzdopen

gzFile gzdopen(int fd, const char *mode);
				
열고자 하는 파일의 이름이 들어가는 대신 파일 지정자가 들어간다는 것만을 제외하고는 gzopen과 동일하게 작동한다. 파일지정자는 open, creat, pipe, filno등으로 생성된다.

실패했을 경우 NULL을 리턴한다.


2.1.6. gzsetparams

int gzsetparams(gzFile file, int level, int strategy);
				
동적으로 압축레벨(level)과 압축정책(strategy)을 변경하기 위해서 사용된다. level은 압축률을 지정하기 위해서 사용된다.

압축정책은 Z_DEFAULT_STRATEGY와 Z_FILTERED, Z_HUFFMAN_ONLY 중 하나를 선택할 수 있다.

성공했을 경우 Z_OK를 실패했을 경우 Z_STREAM_ERROR을 리턴한다.


2.1.7. gzread

int gzread(gzFile file, voidp buf, unsigned len);
				
압축파일 스트림 file로 부터 len크기만큼 읽어서 압축을 푼다음 buf에 저장한다. 만약 파일이 gzip 포맷이 아닐경우 단순히 데이터를 복사한다.

성공했을 경우 압축풀린 데이터의 크기를 되돌려준다. 파일의 끝일 경우 0을 그밖의 에러에 대해서는 -1을 리턴한다.


2.1.8. gzwrite

int gzwrite (gzFile file, const voidp buf, unsigned len);
				
buf로 부터 len크기만큼 데이터를 읽어들여서 압축을 한다음 file에 쓴다. 성공적으로 수행되었다면 입력된 원본 데이터의 크기가 리턴되고, 실패 했을 경우 0이 리턴된다.


2.1.9. int VAgzprintf

int VAgzprintf(gzFile file, const char *format, ...); 
				
포맷 저장을 위한 fprintf(3)를 알고 있을 것이다. 이 함수는 fprintf의 압축버젼이라고 할만하다. 포맷을 받아서 압축한다음에 저장한다. 성공했을 경우 압축에 사용된 데이터의 크기를 리턴하고 실패했을 경우 0을 리턴한다.


2.1.10. gzputs

int gzputs(gzFile file, const char *s);
				
널이 제거된 문자열을 받아들여서 압축한다.

성공했을 경우 문자열의 크기를 그렇지 않을 경우 -1을 리턴한다.


2.1.11. gzgets

char *gzgets(gzFile file, char *buf, int len);
				
압축파일 스트림 file로 부터 데이터를 읽어들여서 압축을 푼다음 문자열을 되돌려준다. 데이터를 읽을 때는 len - 1만큼을 읽어 들이며 개행문자를 만나면 리턴시킨다. fgets(3)의 압축버젼이다.

실패 했을경우 Z_NULL을 리턴한다.


2.1.12. gzputc

int gzputc(gzFile file, int c);
				
문자 c를 unsigned char로 변경한다음 file로 저장한다. 성공하면 입력한 값을 리턴하고 실패했을 경우 -1을 리턴한다.


2.1.13. gzgetc

int gzgetc(gzFile file);
				
압축파일로 부터 1바이트를 읽어들인다. 성공하면 읽어들인 값을 리턴하고 파일의 끝을 만났거나 실패했을 경우 -1을 리턴한다.


2.1.14. gzfluseh

int gzflush(gzFile file, int flush);
				
출력대기 중인 모든 데이터를 파일에 쓴다.

성공했을 경우 Z_OK를 리턴한다.


2.1.15. gzseek

z_off_t gzseek(gzFile file, z_off_t offset, int whence);
				
file에서 gzread나 gzwrite를 이용할 시작 위치를 지정하기 위해서 사용한다. lseek(2)와 비슷하다고 볼수 있다. offset은 압축이 풀린 데이터의 변위를 가르킨다. whencelseek(2)에서와 동일한 값을 사용할 수 있지만 SEEK_END가 지원되지 않는다. whence에 대해서 자세히 알길 원한다면 lseek를 이용한 파일내 위치이동를 참고하기 바란다.

이 함수는 압축된 파일내에서 위치이동을 하는게 아닌 압축이 풀린 데이터내에서의 위치이동을 하게 되므로, 파일이 읽기 위해서 열려 있는 경우 극도로 느리게 작동할 것이다. 만약 파일이 쓰기 위해서 열려 있다면 단지 전방향 이동(forward seek)만 허용 된다.

성공했을 경우 압축이 풀린 데이터의 시작지점에서의 변위값을 되돌려주고 실패 했을경우 -1을 리턴한다.


2.1.16. gzrewind

int gzrewind(gzFile file);
				
파일을 처음으로 되돌린다. 읽기모드로 열었을 때만 사용가능하다. gzrewind(file)는 gzseek(file, 0L, SEEK_SET)와 동일하다.


2.1.17. gzeof

int gzeof(gzFile file);
				
파일에서 끝을 만날경우 1을 리턴한다. 그렇지 않을경우 0을 리턴한다.


2.1.18. gzclose

int gzclose(gzFile file);
				
압축과 관련된 모든 작업이 끝나고 Fluseh까지 성공적으로 수행했다면, gzclose를 호출해서 압축된 파일을 닫고 모든 자원을 해제 해야 한다.


2.1.19. gzerror

const char * gzerror(gzFile file, int *errnum);
				
마지막 에러에 대한 에러 메시지를 되돌려준다. errnum은 zlib에서 사용하는 에러번호다. 만약 에러가 zlib가 아닌 다른 (예를 들어서 파일 시스템)곳에서 발생한 것이라면 errnum은 Z_ERRNO로 채워진다. 이때는 errno를 검사 하도록 한다.


2.1.20. zlibVersion

const char *zlibVersion(void);
				
애플리케이션이 사용하는 zlib버젼을 확인하기 위해서 사용한다. 만약 애플리케이션의 zlib버젼과 zlib.h에 선언된 ZLIB_VERSION버젼의 첫글자가 틀리다면 이 애플리케이션은 zlib호환 문제로 사용할 수 없게된다.

다음과 같은 방법으로 버젼호환여부를 체크할 수 있을 것이다.

...
if (strncmp(zlibVersion(), ZLIB_VERSION, 1) != 0)
{
	printf("Version error\n");
	exit(1);
}
...
				


2.2. 예제

2개의 예제파일을 준비했다. 하나는 특정파일을 압축하는 프로그램이고 또다른 하나는 압축된 파일의 원문 내용을 출력하는 프로그램이다. 매우 간단한 소스 이므로 설명은 주석으로 대신하도록 하겠다.


2.2.1. 파일 압축 예제

예제 : jzip.c

/*
 * 파일명 : jzip.c 
 * 사용법 : # jzip [filename]
 * 압축하고자 하는 filename를 입력하면 filename.gz이라는 압축파일이 
 * 생성된다.  
 */

// zlib 헤더파일  
#include <zlib.h>

// 표준 C헤더파일
#include <stdio.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    char    *filename   = NULL;
    char    *gzfilename = NULL;

    gzFile  *zfp;
    int     fd;
    int     n;
    char    buf[256];
    int     lerrno;

    if(argc !=2)
    {
        printf("Usage : jzip [file name]\n"); 
        exit(0);
    }
    filename = argv[1];
    // 압축파일의 이름은 filename.gz 으로 한다. 
    gzfilename = (char *)malloc(strlen(filename)*sizeof(char));
    sprintf(gzfilename, "%s.gz", filename);

    // 압축원본 파일이 존재하는지 확인한다. 
    if (access(filename, F_OK) != 0) 
    {
        printf("존재하지 않는 파일입니다\n");
        exit(0);
    }

    if ((fd = open(filename, O_RDONLY)) < 0)
    {
        printf("file open error\n");
        exit(0);    
    }

    // 압축파일을 연다. 
    if ((zfp = gzopen(gzfilename, "wb")) == NULL)
    {
        exit(0);
    }

    // 원본파일을 에서 데이타를 읽어들이고 
    // gzwrite함수를 이용해서 데이터를 압축하고 파일에 쓴다.   
    while((n = read(fd, buf, 255)) > 0)
    {
        if (gzwrite(zfp, buf, n) < 0)
        {
            printf("%s\n",gzerror(zfp, &lerrno));
            exit(0);
        }
    }
    gzclose(zfp);
    printf("압축 성공 : %s => %s\n", filename, gzfilename);
}
				
컴파일 할 때는 반드시 zlib를 링크 시켜줘야 한다.
# gcc -o jzip jzip.c -lz
				


2.2.2. 압축 해제 예제

이번 프로그램은 압축된 text문서의 원래 내용을 보여주는 간단한 프로그램이다.

예제 : jcat.c

#include <zlib.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    char *filename = NULL;

    gzFile *zfp;
    char buf[1024];

    if (argc != 2)
    {
        printf("Usage : jcat [file name]\n");
        exit(0);
    }
    filename = argv[1];

    if ((zfp = gzopen(filename, "rb")) == NULL)
    {
        exit(0);
    }

    while(gzgets(zfp, buf, 1023) != NULL)
    {
        printf("%s", buf);
    }

    gzclose(zfp);
}
				


2.3. 네트워크 애플리케이션에서의 활용

zlib는 일반 파일을 압축하기 위한 용도로 매우 훌륭한 도구이긴 하지만 인터넷을 통해 대량의 정보를 보내는 요즘에는 특히 네트워크에서의 데이터 압축을 위해서도 중요하게 사용된다.

대부분의 웹서버와 웹클라이언트는 zlib를 이용해서 자체적으로 필요한 데이터를 압축하고 해제하면서 가능한한 네트워크의 자원을 효율적으로 사용할 수 있도록 작성되어지고 있다. 공용으로 사용되는 네트워크 보다는 아무래도 개인이 사용하는 PC의 (CPU)자원을 활용하는게 여러모로 효율적이기 때문이다.

네트워크 애플리케이션에서 데이터를 압축시켜서 전송하기 위해서는 uncompress, compress등의 압축명령을 주로 사용한다. 이들 내용에 대해서는 자세히 다루지 않을 것이다. 직접 구현해보는 것도 매우 재미있을 것이다. 

반응형

+ Recent posts