레이블이 c인 게시물을 표시합니다. 모든 게시물 표시
레이블이 c인 게시물을 표시합니다. 모든 게시물 표시

2016년 6월 17일 금요일

Python이 진짜 강점을 나타낼 수 있는 분야는? (코딩도장)

Python이 진짜 강점을 나타낼 수 있는 분야는?

출처: 코딩도장

c/c++이 주 사용 언어인 나는 최근 제 2의 언어로써 Python을 채택하려고 하는데 그 목적은 프로그램의 개발속도를 높이자는 것이다.

c/c++은 참으로 강력한 언어이다. 이 언어로는 못하는 일이 없으며 거의 대부분의 제품은 이 언어로 개발 되는 듯하다.
그러나 이 언어는 프로그램 개발이 그리 용의하지 않다. 많은 경험이 필요하다.

좀 놀면서 편하게 coding할 수있는 언어가 필요하다.
그래서 python을 학습하려고 한다.

그런데 python을 학습하기 앞서 다음과 같은 의문이 생겼다.
python은 매번 보기 안좋은 시꺼먼 consol 해석기를 켜놓고 실행시켜야 하는가 ?
이 언어로 c/c++ 로 할수있는 일을 어느 정도로 할 수 있는가 ? (성능은 고려하지 말고)
python이 진정으로 매력을 나타낼 수 있는 일은 어떤 일인가. 내가 알건데 python은 문자열 처리를 위한 간단한 업무에서 좋다고 한다.
python으로 제품을 만들어 판매할 수있는가. 있다면 왜 python 으로 만든 제품이 흔하지 않은가. (나는 개인적으로 아직 한번도 보지 못했다.)
#python #c++ #c

2012/02/14 19:17
작성자: 박철



4개의 풀이가 있습니다.

1>

python은 매번 보기 안좋은 시꺼먼 consol 해석기를 켜놓고 실행시켜야 하는가 ?
말씀하신 파이썬 인터프리터는 간단한 테스트시에만 사용합니다. 보통은 파이썬 스크립트 파일을 작성한 후 파일을 실행시킵니다.

이 언어로 c/c++ 로 할수있는 일을 어느 정도로 할 수 있는가 ? (성능은 고려하지 말고)
제가 c/c++ 에 대해서 잘 알지 못하기 때문에 정확히 말씀드리기 어렵지만 성능을 고려하지 않는다는 단서가 있다면 대부분 할 수 있지 않을까? 라는 생각이 드네요.
또한 파이썬과 c/c++은 함께 사용할 수 있습니다. 파이썬을 글루(Glue)언어라고도 부르는 이유가 여기 있습니다. c, c++ 로 만든 프로그램을 파이썬에서 약간의 노력만으로 쉽게 사용할 수 있습니다. 보통 성능이 요구되는 쪽은 c, 그 외의 것은 파이썬.. 뭐 이런 조합이 많이 사용되는 것 같습니다.
간단한 예제 - http://docs.python.org/extending/extending.html#a-simple-example

python이 진정으로 매력을 나타낼 수 있는 일은 어떤 일인가. 
내가 알건데 python은 문자열 처리를 위한 간단한 업무에서 좋다고 한다.
파이썬을 활용할 수 있는 곳이 많기 때문에 무엇에 가장 좋다고 꼬집어 말하기 힘들군요.. 저 같은 경우에는 산출물 작성시에도 파이썬을 사용하고 이런저런 간단한 유틸리티성 프로그램을 만들때에도 파이썬을 많이 활용하는 편입니다.
하지만 파이썬의 대세는 뭐니뭐니 해도 웹 쪽이 아닐까 싶네요. 파이썬으로 만들어진 웹 프레임워크를 이용하여 개발되는 온라인 서비스들이 무척 많습니다.

python으로 제품을 만들어 판매할 수있는가. 있다면 왜 python 으로 만든 제품이 흔하지 않은가. (나는 개인적으로 아직 한번도 보지 못했다.)
위키피디아에 파이썬 관련한 소프트웨어가 잘 정리되어 있습니다.
http://en.wikipedia.org/wiki/List_of_Python_software
Dropbox, Mercurial등 눈에 띄는 것이 몇개 보이네요..
그리고 구글이 Gmail, Google-Groups, Google Maps등에서 backend 웹 어플리케이션으로 파이썬을 사용한다고 하네요.
그리고 결정적으로 지금 보고 계시는 코드잡이 바로 파이썬으로 만들어진 django기반에서 돌고 있습니다. ^^
2012/02/15 10:09
작성자: 길가의풀

2>

저희 온라인 게임은 Python으로 개발되었습니다. 엔진 Core는 C++로 작성되었지만, Python 바인딩되어있어서 AI나 클라이언트 로직이 다 Python으로 작성되어 있습니다. 그래서 무척 빠르고 쉬운 개발을 할 수 있습니다.
http://www.panda3d.org/

2012/02/16 02:37
작성자: gyedo
이걸보니 파이썬의 대세는 웹이 아니라 게임이군요 ^^ - 길가의풀, 2012/02/16 08:54
사실 대세는 아니고, 이런 것도 할 수 있다죠 ^^; - gyedo, 2012/02/16 09:06
와~~ 좋은 정보 감사드립니다. ^^ - 오랑캐꽃, 2012/02/16 15:05
앗 판다3d 네요. 예전에 학원다닐 때 빠른 프로토타입 개발이란 주제로 외부 강연 오신분이 소개해 주셨었던 기억이 나네요. - 책읽는아이, 2012/02/17 18:59


3>

(답변과 관계 없는 개인적인 생각입니다만, 서로 다른 질문은 하나씩 나눠서 따로 올리는 게 답변이나 토론에 더 이로울 것 같습니다.)

Ⅰ) 파이썬 기본 해석기(interactive interpreter)는 그다지 쓸만한 건 아닙니다(없는 것보다는 낫습니다). 보통은 IPython 같이 좀 더 잘 정리된 셸을 쓰는 게 더 좋습니다. IPython 같은 경우 SAGE라고 하는, 파이썬을 기반으로 매쓰매티카 같은 컴퓨터 대수학 시스템(CAS)을 구현하는 곳에서 만들었는데 이 때문에 별의별 기능이 다 들어 있습니다. 제가 이런 류의 셸을 많이 쓰는 건 아니라서 (유닉스 셸에 익숙해서 readline만 있으면 충분합니다) 실제로 뭘 쓰는 게 좋은지는 좀 찾아 보셔야 하겠습니다.

Ⅱ) 속도를 고려하지 않는다면 뭐든지 할 수 있고, 속도가 필요하다면 PyPy를 쓰시면 됩니다. 기본적으로 C/C++로 코드를 투자하는 만큼 성능 향상이 이루어지지 않는다면 파이썬을 쓰는 게 유리합니다. (I/O 같이 언어 구현 성능에 별 영향을 안 주는 곳에서는 이미 C/C++가 많이 힘을 잃었고요, PyPy 같은 경우 C/C++랑 비교해서 많아야 3~5배 느린 경우가 태반이니까요.)
파이썬으로 프로토타이핑을 한 뒤에 필요한 부분만 C/C++로 대체하는 방법도 있습니다. 특히 파이썬 2.5 이후로 ctypes 외부 함수 라이브러리(FFI)가 기본으로 들어 가면서 옛날에 C 모듈을 만들어서 외부 라이브러리랑 통신해야 했던 걸 순수 파이썬만으로도 대부분 할 수 있게 되었습니다. 이를테면 그래픽이나 사운드, 멀티미디어 등의 기능을 거의 모두 순수 파이썬으로 구현한 pyglet 같은 것들이 있지요. (처음에 OpenGL 바인딩을 파이썬만으로 짠 걸 보고 경악했던 기억이 있습니다...) 네트워크 쪽에서는 Twisted가 독보적인 위치를 차지하고 있고, 웬만한 프로토콜은 순수 파이썬 구현이 모두 존재합니다(예를 들어서 SSH 클라이언트 라이브러리인 paramiko가 순수 파이썬 구현입니다).
Ⅲ) "파이썬으로 할 수 있다"가 아니라 "파이썬으로 하는 게 유리하다"는 쪽을 생각한다면, 대부분의 웹 프로그래밍과, 프로토타이핑이 중요한 대부분의 업무에서 유리한 것 같습니다. 저 같은 경우 C/C++ 같은 언어로 해야 할 일을 파이썬으로 먼저 샘플로 만들어 보고 나중에 C/C++로 바꾸는 경우도 많고, 심지어 파이썬으로 만든 게 충분히 좋아서 그대로 가는 경우도 많았습니다. 파이썬 말고 루비 같은 언어도 이런 장점을 대부분 지니고 있지만, 파이썬의 경우 라이브러리의 품질이 분야에 상관 없이 전반적으로 충분히 좋아서 임의의 용도로 쓰기에 더 나은 듯 합니다. 다만 파이썬을 외부 배포를 위해 패키징할 경우 (특히 윈도의 경우) 몇 가지 주의해야 할 점이 있다는 걸 아셨으면 합니다.

Ⅳ) 오픈 소스 소프트웨어에서는 매우 흔한 일이고요, 오픈 소스 소프트웨어가 아닌 판매용 독점(propietary) 소프트웨어를 기준으로 생각해도 여러 사례가 있습니다. 지금은 코렐이 인수한 Paint Shop Pro가 파이썬을 스크립팅 언어로 쓰고(Maya도 비슷한 걸로 기억합니다), 비트토런트의 옛 클라이언트가 파이썬으로 작성되어 있었습니다. (앞에서 말한 프로토타이핑의 성공적인 사례라 할 수 있습니다.) 게임 업계에서도 파이썬을 많이 쓰는데, 대부분 스크립팅 또는 약간 더 드물게 서버 소프트웨어로 씁니다만 이브 온라인 같이 클라이언트에까지 파이썬을 쓰는 경우도 있습니다. (스택리스 파이썬이라고 하는 특수 구현을 쓰긴 합니다.)

2012/02/18 21:33

작성자: 아라크넹
아, 답변에 까먹었습니다만 문자열 처리는 파이썬도 많이 쓰지만 펄을 쓰는 경우도 많습니다. 자연어 처리(NLP) 분야에서는 현재 파이썬과 펄의 사용이 대략 반반 정도 되는 것 같습니다. - 아라크넹, 2012/02/18 21:34

4>

조금 여담이지만, 파이썬을 이용할 경우 하둡등 분산 처리 시스템과 연동도 쉬울뿐만 아니라, 파이썬 자체가 강력한 수학, 통계 라이브러리를 가지고 있기 때문에, 빅데이터 분석 과제에 자주 사용하는 편입니다... ㅎ

2013/09/23 16:37

작성자: Lee OJoun





파이썬에서 C모듈을 이용하기

"파이썬에서 C모듈을 이용하기"

*출처 : http://joyholic.kr/122


파이썬은 그 자체가 C로 작성되었다.

이런 맥락에서 생각해보면 파이썬에서 C모듈을 사용할수도 있을뿐 아니라 C에서 파이썬 모듈을 사용할수도 있을것 같다. -> 이말은 사실이다.

본론으로 들어가기전에 파일의 확장자에 대해 살포시 살펴보기로 하자.

파일의 확장자는 그 파일의 속성과 종류를 손쉽게 알려주는 역할을 한다. (이는 어떻게 보면 있어도 되고 없어도 되는 존재이며, 관습적으로 사용하게 된다.)

우리가 흔히 알고 있는 확장자로는 'exe', 'txt', 'jpg', 'mp3' 등 수없이 많다.
  • lib : 정적 라이브러리 파일, 일반적으로 C, C++ 라이브러리다.
  • dll : 동적 라이브러리 파일
  • pyd : 파이썬이 사용하는 C 라이브러리

여기서는 C로 작성된 코드를 이용하여 pyd를 만들고 이 pyd를 이용해서 C 모듈을 이용하는 방법에 대해서 살펴본다.

1. 파이썬 코드에서 import를 하면 일어나는 일


파이썬에서 특정한 모듈을 사용하기 위해 import문을 이용하여 모듈을 지정한다. 파이썬이 사용하고 로드하는 모듈은 아래의 순서로 찾고 그와 일치하면 모듈을 초기화한 후에 지역 이름공간에 이름을 정의한다.
  • 기본적으로 탑재되어 있는 모듈들 : ex)socket, time 등
  • C로 만들어진 dll 파일(윈도에서는 pyd 파일)
  • 자체 제작한 파이썬 모듈

그러면 파이썬에서 import는 어떤 매커니즘으로 동작하는가? 이것을 함 알아보자.
import mylib

위의 코드를 수행할때 파이썬은 mylib 모듈을 로드하기 위해 다음의 절차에 따라 검색한다.
  1. sys.modules 에 등록되어 있는지 확인한다. 등록되어 있으면 로드한다.
  2. (1)에서 존재하지 않으면 sys.path의 디렉토리를 검색하면서 mylib 모듈을 찾는다.
다시 한번 요약하면, 외부 모듈을 import 하게되면 파이썬은
  1. 모듈이름과 일치하는 모듈을 찾는다.
  2. 모듈을 초기화 한다.
  3. 모듈이름을 지역이름공간에 할당한다.

2. 파이썬에서 이용할 C 모듈 만들기

여기서 우리는 우리가 만든 C 모듈을 파이썬에서 사용하고 싶다. 즉,
import (우리가 만든 C 모듈) 하고 싶다 이 말이다.

우리가 C로 만든 모듈을 파이썬에서 이용하고자 할때 역시 위에서 서술한 import 절차에 따라 모듈을 찾고 로드하게 된다.

우리가 유의해야 할 곳은 바로 모듈을 초기화 하는 부분이다. 나머지 절차는 파이썬이 다 알아서 해준다.

파이썬에서 사용하는 C모듈은 리눅스에서는 .so 확장자 형태로, 윈도에서는 .pyd 확장자 형태로 빌드되어 파이썬 패키지 디렉토리의 Lib/site-package 디렉로리로 복사되므로서 사용가능하게 된다.
  1. C로 소스코드를 작성한다.
  2. 작성한 C 소스코드를 컴파일하여 파이썬이 이용가능한 .so (리눅스에서) 및 .pyd(윈도용) 파일로 빌드한다.
  3. .so 및 .pyd 파일을 파이썬 디렉토리의 Lib/site-package로 복사한다.

그러면 하나의 실제 C모듈을 만들어 적용하므로서 설명해보자.

파이썬이 C 모듈을 import 하게되면 초기화를 위해 "init+모듈이름" 으로 된 함수를 호출한다. 모듈이름이 mylib 이면 initmylib()을 호출한다는 말이다.

그러므로 우리의 C 소스에는 위에서 설명한 초기화를 위한 함수 init+모듈이름 으로 된 함수가 존재해야만 한다.

예제로 만들 모듈은 입력한 문자열을 pylog.txt 라는 파일에 기록하는 모듈이다. 주석을 달았으므로 주석을 참조하도록 한다.

// mylib.c
#include "Python.h"
#include <stdio.h>
static PyObject *ErrorObject;
// 실제 동작하는 함수
static PyObject* write_log(PyObject *self, PyObject *args) // 인자는 이와같이 고정된다.
{
  char* msg;
  FILE *fp;
  if(!PyArg_ParseTuple(args, "s", &msg))
    return NULL;
  fp = fopen("c:\\pylog.txt", "wt+");
  fprintf(fp, msg);
  fclose(fp);
  return Py_BuildValue("i", 0);
}

/* methods 구조체 배열에 지정되는 정보는 {"실제사용할 메쏘드명", 메쏘드명에 대응하는 실제 동작하는 함수명, 인자 종류} */
static struct PyMethodDef methods[] =
{
  {"wlog", write_log, METH_VARARGS},
  {NULL, NULL}
};

//
void initmylib()
{
  PyObject* m;
  // Py_InitModule("모듈명", 이모듈에 적용된 메쏘드들을 담을 구조체배열 포인터)
  m = Py_InitModule("mylib", methods);
  ErrorObject = Py_BuildValue("s", "error");
}

위의 C 코드를 컴파일하기 위해 setup.py 함수를 작성해야 한다.

# setup.py
from distutils.core import setup, Extension
setup(name = "mylib",
  version = "1.0",
  description = "print log",
  author = "Samsjang",
  author_email = "samsjang@cdnetworks.co.kr",
  url = "http://www.cdnetworks.co.kr",
  ext_modules = [Extension("mylib", ["mylib.c"])]
)

위의 소스에서 ext_modules에는 적용할 모듈명과 그 모듈의 구현코드를 기록한다. 나머지 부분의 기타 정보를 기록해둔 메타정보에 불과하다.

리눅스 및 윈도에서 아래와 같이 빌드하고 설치한다.
python setup.py install

위의 명령은 리눅스에서는 gcc를 호출하며, 윈도에서는 visual C++ 컴파일러가 구동되면서 빌드한 후 파이썬 디렉토리의 Lib/site-packages 디렉토리에 mylib.so 또는 mylib.pyd 파일이 생성된다.

자 그럼 C모듈인 mylib 모듈을 실제 파이썬 코드에서 적용해보자.

# test.py
import mylib
if __name__ == '__main__':
mylib.wlog('I love U')

위의 결과는 C:\\pylog.txt 파일에 "I love U" 라는 글을 남긴다.



2016년 2월 23일 화요일

C언어 CallBack 함수를 이용한 최대/최소 구하기 Example


출처: 네이버지식인
#include <stdio.h> 
#include <conio.h> 
typedef int (*CALLBACK)(int); 
int Callback_function(CALLBACK pCallback,int n) 
{ 
   return pCallback(n); 
} 
int input(int i) 
{ 
   int ar[10]; 
   int j=0; 
   int sum=0; 
   int min=999, max=-999; 
   for(j=0;j<i;j++){ 
      printf("%d번의 숫자를 입력하시오 :",j+1); 
      scanf("%d",&ar[j]); 
      if(ar[j]<min) 
      min=ar[j]; 
      if(ar[j]>max) 
      max=ar[j]; 
      sum=sum+ar[j]; 
   } 
   printf("최대값은 %d, 최소값은 %d 평균은 %d입니다.\n", max,min,sum/j); 
   return 0; 
} 
int modify(int i) 
{ 
   if(i>10) 
   { 
      printf("숫자를 다시 입력해 주세요. 10까지만 되요 :"); 
      scanf("%d",&i); 
      return modify(i); 
   } 
   else        return i; 
} 
void main() 
{ 
   int i; 
   printf("몇명 까지의 정보(최대값,최소값,합,평균)를 원하십니까? "); 
   scanf("%d",&i); 
   i=Callback_function(modify,i); 
   Callback_function(input ,i); 
   _getch(); 
} 

(실행결과화면)

2016년 2월 4일 목요일

MagickWand C-API (ImageMagick v6)

MagickWand API

이미지 유사성에 대한 문제가 있어 이래저래 API를 알아보고 있었다.
워낙 이미지쪽은 잘 몰라서 손안대던 분야이기도 하다.
막상 보고나니 이거 원.. 세상 참 황홀하다;ㅋㅋㅋㅋㅋ 재밌네;

라이브러리 설치는 매우 간단했다.
먼저 http://www.imagemagick.org/script/binary-releases.php 에 방문하여 라이브러리를 다운로드 한다. ( Unix or Windows )

나는 리눅스에서의 작업이라 Unix Binary Release에서 다운로드 받았다.


설치는 바로 아래에 잘 설명되어 있다. 그대로만 따라하면 끝.ㅋ

rpm -Uvh ImageMagick-6.9.3-2.i386.rpm
(이건 RPM 설치. 직접 다운받았다면 아래과정으로 바로.)

cd $HOME
설치디렉터리를 선정한다. 예를들어 /home 이라면

tar xvzf ImageMagick.tar.gz
다운로드 받은 압축파일을 압축해제한뒤

vi ~/.bash_profile
환경변수선언을 위해 .bash_profile 을 열어

...
export MAGICK_HOME="$HOME/ImageMagick-6.9.3"
...
export PATH="$MAGICK_HOME/bin:$PATH
...
LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$MAGICK_HOME/lib 
export LD_LIBRARY_PATH
...
위와 같이 MAGICK_HOME을 설정한 후 PATH에 넣어주고, 라이브러리를 등록시킨다.

끝. ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

convert logo: logo.gif
identify logo.gif
display logo.gif
자, 위와같이 해보아서 logo.gif가 생성되면 성공.ㅋ


흠.. 이제부터 내 목표.

난 이미지편집을 하기 위해 이 라이브러리를 설치한게 아니다.
특정 이미지의 정보들을 얻길 원한다.

identify -verbose a.gif
(이 명령에서 얻는 정보들을 원한다고나 할까? ^^;)

먼저 MagickWand API 의 Method들 부터 확인했다. 관련정보는 http://imagemagick.org/script/magick-wand.php 에 가보면 볼 수 있다.

그리고 그 아래에는 간단한 예들이 나오고 컴파일 방법등이 보였다.
매우 간단하게 설명되어있어 좋았다.ㅋ
좀더 많은 예를 보려면 MagickWand Examples in C를 가란다.

하지만 난 Image statistic 정보를 원하기에 http://www.imagemagick.org/api/statistic.php 이곳으로 이동하여 각 메소드들를 확인해보았다. 그런 와중에 두가지 뻘짓을 하게 되었다.
첫째, 채널간 평균데이터를 보고싶었는데 어찌해야하는가?
둘째, 표준편차와 최소/최대값이 매우 큰 수가 결과로 나온다. 왜?

채널정보를 예를들면,
이미지 內 각 채널의 '첨도'를 구하고 싶다면 GetImageChannelKurtosis를 보면된다.
채널 평균값은 GetImageChannelMean 를 보면된다.

난 전반적인 채널정보를 원한다. 그래서, GetImageChannelStatistics를 이용했다.

channel_statistics=GetImageChannelStatistics(image,exception);
red_mean=channel_statistics[RedChannel].mean;

예제상, RedChannel의 경우 Red 채널의 정보가 나오니, Blue는 당연히 BlueChannel 일 테고, Green은 GreenChannel 일 것이다. 더불어 CMYK(Cyan, Magenta, Yellow, Black=Key)도 가능했고 GrayChannel도 가능했다.

헌데, 난 전체의 종합적인 정보, 즉, identify -verbose {filename} 에서의 Overall 데이터를 원했지만, 이게 당췌 어디로 들어가는지;; 해서 삽질하다 찾아낸게 이거다.

CompositeChannels


kurtosis = channel_statistics[CompositeChannels].kurtosis; //첨도
skewness = channel_statistics[CompositeChannels].skewness; //왜도
mean = channel_statistics[CompositeChannels].mean; //채널평균
standard_deviation = channel_statistics[CompositeChannels].standard_deviation; //표준편차
여기서 두번째 문제가 벌견되었다.
표준편차나 최소/최대값의 경우 identity -verbose 의 결과와는 달리 이상하게 큰 수가 보여지는 것이다. 이때문에 한 3시간 삽질한 것 같다. 아오ㅋㅋㅋㅋㅋㅋㅋ

ChannelStatistics와 identity -verbose의 결과들을 모아놓고 침착하게 보았더니 이상하게 일정배수로 기록된거 아닌가 하는 의심을 가지게 되었다. 마치 256배씩 나오는것 같았다. 1~2값이 나오는 경우를 보고 응? 했던 것.ㅋㅋㅋㅋ

256컬러


256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 256컬러, 갑자기 머리속을 채우는 256컬러.아오;ㅋㅋㅋㅋㅋㅋ

결국 결과를 256으로 나누니 정상적인 값이 보여지기 시작했다. (아오삽질사랑;ㅋ)




256....

2016년 1월 19일 화요일

[C] 구조체 quick sort 하는 법. (stdlib의 qsort를 이용)

#include <stdlib.h>

typedef struct _QUESTION {
    int n;
    char *txt;
} QUESTION;

int compare(const void *A, const void *B);


int main()
{
    QUESTION q[10] = {0, };
    qsort(q, 10, sizeof(QUESTION), compare);

    return 0;
}


int compare(const void *A, const void *B)
{
    QUESTION *ptA = (QUESTION *)A;
    QUESTION *ptB = (QUESTION *)B;

    if (ptA->a < ptB->a) return -1;
    else if (ptA->a > ptB->a) return 1;
    else return 0;
}

2015년 11월 25일 수요일

Date/time manipulation IN OCIlib

Date/time manipulation

Detailed Description

OCILIB encapsulates Oracle SQL Date datatype within OCI_Date structure
Basically, the OCI_Date routines are wrappers around the Oracle OCIDate APIs

Example

#include "ocilib.h"#define SIZE_STR 260

int
main(void)
{
  OCI_Date *d1, *d2;
  char str[SIZE_STR+1];
  if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT))
    return EXIT_FAILURE;

  d1 = OCI_DateCreate(NULL);
  d2 = OCI_DateCreate(NULL);
  strcpy(str, "13041978 20:20:12");

  OCI_DateFromText(d1, str, "DDMMYYYY HH24:MI:SS");
  OCI_DateToText(d1, "DD/MM/YYYY HH24:MI:SS", SIZE_STR, str);
  printf("\nDate is %s\n", str);

  OCI_DateSysDate(d1);
  OCI_DateToText(d1, "DD/MM/YYYY HH24:MI:SS", SIZE_STR, str);
  printf("\nSysdate is %s\n", str);

  OCI_DateAddDays(d1, 5);
  OCI_DateAddMonths(d1, 2);
  OCI_DateToText(d1, "DD/MM/YYYY HH24:MI:SS", SIZE_STR, str);
  printf("\nDate + 5 days and 2 months is %s\n", str);

  OCI_DateAssign(d2, d1);
  OCI_DateLastDay(d1);
  OCI_DateToText(d1, "DD/MM/YYYY HH24:MI:SS", SIZE_STR, str);
  printf("\nLast day of the month : %s\n", str);
  printf("\nNumber of days until the end of the months : %i\n", OCI_DateDaysBetween(d1, d2));

  OCI_DateFree(d1);
  OCI_DateFree(d2);
  OCI_Cleanup();

  return EXIT_SUCCESS;
}

2015년 9월 8일 화요일

about Obstacks



(이 글은 GNU C Library에 포함되어 있는, obstack에 대하여 설명합니다.)


Obstack 소개

Obstack은 Object Stack의 약자로서, 일종의 small memory allocator입니다. 대개의 C/C++ 책을 보면, 작은 크기의 메모리를 여러번 할당할 필요가 있을 경우, malloc(3)이나 new operator를 직접 쓰는 것보다, 따로 메모리 할당 루틴을 만들어 쓰는 기법을 소개하곤 합니다. 물론 잘 만들면 좀 더 나은 성능을 가진 small memory allocator를 만들 수 있겠지만, 이미 GNU C library에 포함되어 있기 때문에, obstack을 쓰는 것이 좀 더 현명한 선택이 될 수 있습니다. (Why reinvent the wheel?)
Obstack은 GNU C library에 포함되어 있습니다. 좀 더 정확히 말하면, GNU libiberty library에 포함되어 있으며, 이 libiberty library는 GNU C library나 GCC, GDB 등의 소스에 포함되어 있는 라이브러리입니다. 필요한 소스는 단지 obstack.h와 obstack.c이기 때문에, GNU system이 아닌 다른 시스템에 포팅하기도 매우 쉽습니다.
글쓴이의 개인적인 경험을 바탕으로 말하자면, Obstack은 매우 이식성이 높습니다. 글쓴이는 obstack을 Windows, DOS(Turbo C 2.0), vxworks, psos등에 포팅한 경험을 갖고 있으며, 이 때, 소스 수정은 거의 필요없었습니다. 또한 시스템이 제공하는 memory allocator가 매우 느릴 경우, 또는 overhead가 클 경우등의 상황에서 obstack을 써서 큰 효과를 보았습니다.
GNU obstack은 malloc(3)과 다른 여러 특징을 가지는데, 크게 요약하면 다음과 같습니다:
  • memory를 (블럭 단위로) 미리 할당해 놓고, 사용자가 요청할 때 그 블럭의 일부분을 쪼개어 그 일부분을 제공합니다. 따라서 malloc(3)에 비해, 함수 호출에 대한 overhead가 무척 작습니다.
  • obstack을 써서 할당한 메모리는 이름을 보면 알 수 있듯이, stack 형태로 할당됩니다. 그리고, 기존에 할당되어 있던 메모리를 해제하면, 그 이후에 할당했던 메모리는 자동으로 해제됩니다. 따라서, obstack을 써서 N 번 메모리를 할당했을 경우, 맨 처음에 할당받은 메모리를 해제(free)하게 되면, N개의 메모리 블럭이 모두 해제(free)됩니다.
  • obstack의 growing object 기능을 쓰면, 메모리를 단계적으로 할당할 수 있습니다. 예를 들어, 한 object의 크기를 필요에 따라 조금씩 줄이거나 늘려 할당한 다음, 마지막에 완전히 크기가 결정되었을때 최종 메모리 크기를 결정할 수 있습니다.
  • obstack의 대부분 기능은 매크로 형태로 제공되기 때문에, 매우 빠릅니다.
  • 한가지 단점은, obstack이 내부적으로 일정한 memory block을 할당해서 나눠주기 때문에, 개발자가 주의하지 않을 경우, 메모리 블럭이 망가질 가능성이 있다는 것입니다. 이런 경우, efence와 같은 메모리 디버깅 라이브러리는 큰 도움을 주지 못합니다.

Obstack 써보기

Obstack을 쓰기 위해서는, 먼저 기본적인 memory allocator를 알려 주어야 합니다. 개발자는 매크로 obstack_chunk_alloc과 obstack_chunk_free를 각각 정의해주어야 하는데, 간단히 다음과 같이 써 주면 됩니다:
#define obstack_chunk_alloc malloc
#define obstack_chunk_free  free
물론, obstack 헤더 파일을 포함하는 코드도 써 주어야 할 것입니다 (위 매크로 정의와 #include의 순서는 상관 없습니다):
#include <obstack.h>
일단 위와 같이 환경 설정이 끝났다면, 이제 obstack을 하나 만들어야 합니다. (상황에 따라 여러 개 만들 수도 있습니다.) obstack을 만드는 대표적인 함수는 obstack_init()입니다. 다음과 같이 obstack을 만들 수 있습니다:
struct obstack stack;
obstack_init(&stack);
obstack_init()은 내부적으로 메모리 블럭을 하나 만들고, 기타 초기 설정을 마치는 함수입니다. 만약 obstack_init()이 실패했을 경우, 전역 변수인 obstack_alloc_failed_handler에 저장된 함수 포인터를 호출해서 에러 상황을 알리게 됩니다. 개발자가 특별히 이 변수에 에러 처리 함수를 등록하지 않았다면, 기본적으로 에러를 출력하고 프로그램을 종료하게 됩니다.
주어진 obstack에 메모리를 할당하는 함수는 여러개가 존재합니다. 이 중 가장 대표적인 함수는 obstack_alloc()이며, malloc(3)과 같은 기능을 한다고 생각하시면 됩니다. 예를 들어, 문자열을 복사하는 함수인 strdup()과 비슷한 함수를 다음과 같이 만들 수 있습니다 (아래 코드는 GNU C Library Manual에서 인용한 것입니다):
struct obtsack string_obstack;
 
char *
copystring(char *string)
{
  size_t len = strlen(string) + 1;
  char *s = (char *)obstack_alloc(&string_obstack, len);
  memcpy(s, string, len);
  return s;
}
이 외에도 다양한 할당 함수가 제공됩니다:
/* SIZE 크기의 메모리를 할당하고, ADDRESS에서 그 크기만큼의 내용을 읽어다 복사해줍니다.
 * 따라서 할당하고 나서, 초기값을 설정하는데 쓸모있는 함수입니다. */
void *obstack_copy(struct obstack *OBSTACK_PTR, void *ADDRESS, int SIZE);
 
/* obstack_copy()와 비슷하지만, SIZE + 1의 크기를 할당하고 마지막 한 바이트를
 * '\0'으로 채운다는 것만 다릅니다. 메모리를 할당하고 문자열을 집어넣을 때 쓸모있습니다. */
void *obstack_copy0(struct obstack *OBSTACK_PTR, void *ADDRESS, int SIZE);
앞에서도 잠깐 이야기했지만, obstack에 있는 메모리를 해제(free)하는 것은, malloc(3)/free(3)와 좀 다르게 동작합니다. 일단 메모리를 해제하는 함수는 obstack_free()입니다.
void obstack_free(struct obstack *OBSTACK_PTR, void *OBJECT);
이 함수는 주어진 obstack에 있는 OBJECT와, 이 OBJECT 이후에 할당한 모든 메모리를 해제합니다. 만약 OBJECT 파라메터에 NULL을 주면, 이 obstack에 할당된 모든 OBJECT가 해제(free)되며, 이 obstack은 더이상 쓸 수 없는 상태가 됩니다. 따라서 모든 메모리를 해제하면서, 동시에 이 obstack을 나중에 다시 쓰기 위해서는, 이 obstack에 맨 처음 할당했던 메모리 주소를 기억해 두었다가 OBJECT 파라메터에 전달해야 합니다.
예를 들어, 포인터 A, B, C가 있고, 각각 메모리를 10, 100, 1000 바이트씩 순서대로 할당해서 썼다고 가정해 봅시다. 이 때 이 모든 메모리를 해제하기 위해서는 다음과 같이 호출하면 됩니다:
struct obstack my_stack;
 
void *A, *B, *C;
A = obstack_alloc(&my_stack, 10);
B = obstack_alloc(&my_stack, 100);
C = obstack_alloc(&my_stack, 1000);
/* ... */
obstack_free(&my_stack, A);
앞에서 말했듯이, 한 obstack에 있는 메모리 블럭을 해제하면, 그 obstack에서 이 메모리 블럭 이후에 할당한 모든 메모리까지 다 해제된다는 것을 다시 한 번 기억하기 바랍니다.

Growing Objects

Obstack은 단계적으로 메모리 블럭을 할당할 수 있는 방법을 제공합니다. 예를 들어, 파일에서 한 token을 읽어서 메모리에 할당한다고 가정해 봅시다. 보통 token을 나타내는 문자열을 다 읽어오기 전에는, (크기를 모르기 때문에) 메모리를 할당할 수 없습니다. 그러나 obstack을 쓰면, 조금씩 메모리를 얻어 쓰다가, 마지막에 크기를 알게 된 순간에 지금까지 얻어쓴 크기만큼 메모리를 할당할 수 있습니다. 이 기능은 특히, 크기를 모르는 text를 파일/네트웍에서 받아 처리하는 함수를 작성할 때 매우 쓸모있습니다.
growing object를 처리하는 함수들은 앞에서 설명한 함수들과는 조금 다른 방식으로 동작합니다. 먼저, 조금씩 얻어쓰는 단계에서는 마지막에 고정될 메모리의 주소를 알 수 없습니다. 즉, 얻어쓰는 단계에서 메모리의 위치가 바뀔 수도 있다는 뜻입니다. 표준 C 라이브러리가 제공하는 realloc(3)을 생각하시면 이해하기 쉬울 것입니다.
한 obstack에서, growing object는 단 하나만 만들 수 있다는 것을 주의하기 바랍니다.
growing object를 위해, 메모리를 할당하는 함수는 매우 많습니다. 여기서 적당한 것을 골라 쓰시면 되며, 여러번 부르거나 섞어써도 상관없습니다.
/* 주어진 크기, SIZE만큼 메모리를 추가, 메모리 초기화 안함 */
void obstack_blank(struct obstack *OBSTACK_PTR, int SIZE);
/* SIZE만큼 메모리를 추가, DATA가 가리키는 곳의 값으로 초기화 */
void obstack_grow(struct obstack *OBSTACK_PTR, void *DATA, int SIZE);
/* obstack_grow()와 같음, 단 SIZE + 1의 크기를 추가하며, 
 * 마지막에 '\0'을 자동으로 추가. */
void obstack_grow0(struct obstack *OBSTACK_PTR, void *DATA, int SIZE);
/* 문자 C를 추가 */
void obstack_1grow(struct obstack *OBSTACK_PTR, char C);
/* 포인터 값 DATA를 추가 */
void obstack_ptr_grow(struct obstack *OBSTACK_PTR, void *DATA);
/* 정수 값 DATA를 추가 */
void obstack_int_grow(struct obstack *OBSTACK_PTR, int DATA);
따로 예제는 만들지 않겠습니다. 다만 obstack_blank()의 경우, 위에서 설명한 것 이외의 기능을 가지고 있습니다. 위 함수들을 써서 메모리를 조금씩 얻는 도중, 일정 크기의 메모리를 다시 반납하고 싶다면 obstack_blank()의 SIZE 파라메터에 음수값(negative value)을 주면 됩니다.
그리고, 나중에 메모리의 크기를 확실히 알았다면, 이제 지금까지 얻어썼던 메모리를 고정(fix)시켜야 합니다. 이 역할은 obstack_finish()하며, 이 때에, 실제 메모리의 주소가 결정됩니다.
void *obstack_finish(struct obstack *OBSTACK_PTR);
만약, 얻어쓰는 도중에, (임시적으로 사용하고 있는) 메모리의 주소를 알고 싶다면, osbtack_base()를 쓰면 됩니다. 또, 현재 얻어쓰고 있는 메모리의 총 크기를 알고 싶다면 obstack_object_size()를 쓰면 됩니다. 만약 obstack_object_size()가 0을 리턴한다면 현재 얻어쓰고 있는 메모리가 없다는 뜻입니다. 주의할 것은, 만약 현재 얻어쓰고 있는 메모리가 없을 경우, obstack_base()가 NULL을 리턴하지 않는다는 것입니다. 얻어쓰고 있는 메모리가 없을 경우 obstack_base()는, 다음에 할당할 메모리 위치를 리턴합니다. 따라서, 현재 얻어쓰고 있는 메모리가 있느냐 여부는 obstack_object_size()로 알아내는 것이 좋습니다.
/* 현재 growing object가 (임시로) 위치하고 있는 주소 리턴 */
void *obstack_base(struct obstack *OBSTACK_PTR);
/* 현재 growing object의 크기 리턴, 없을 경우 0 리턴 */
int obstack_object_size(struct obstack *OBSTACK_PTR);
마지막으로, growing object를 쓴 완전한 예제를 보고 끝내겠습니다. 표준 입력(stdin)에서 텍스트를 읽어서, 띄어쓰기 단위로 한 단어를 읽은 다음, obstack에 할당하고, 이를 리턴하는 함수인 get_word()를 만들겠습니다.
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <obstack.h>
 
#define obstack_chunk_alloc malloc
#define obstack_chunk_free  free
 
struct obstack stack_;
struct obstack *stack;
 
char *
get_word(void)
{
  int ch;
 
  /* 따로 growing object가 있어서는 안됨 */
  assert(obstack_object_size(stack) == 0);
 
  while ((ch = getchar()) != EOF)
    /* 여분의 공백 문자 skip */
    if (!isspace((unsigned char)ch))
      break;
 
  while (ch != EOF) {
    if (isspace((unsigned char)ch))
      break;
 
    /* 다음 공백 문자 또는 EOF 전까지 growing object로 추가 */
    obstack_1grow(stack, ch);
    ch = getchar();
  }
 
  if (obstack_object_size(stack) == 0)
    return NULL;
 
  /* 완성된 단어를 할당하고, 리턴 */
  return obstack_finish(stack);
}
 
 
int
main(void)
{
  char *word;
 
  stack = &stack_;
  obstack_init(stack);
 
  while ((word = get_word()) != NULL)
    printf("word: %s\n", word);
 
  obstack_free(stack, NULL);
  return 0;
}

Memory Usage

Obstack은 내부적으로 블럭 단위(보통 4096 byte)로 메모리를 할당해서, 사용자가 요청할 때 쪼개어 보내줍니다. 따라서 동적으로 메모리가 할당되는 과정을 지켜보면 계단식으로 메모리가 요청된다는 것을 예상할 수 있습니다. 아래 그래프는 위 프로그램을 실행시켰을 때, 메모리가 할당되는 과정을 보여줍니다. (빨간색 선이 동적으로 할당되는 메모리 크기입니다)
Obstack-memusage.png

기타사항

이외에도 obstack은 여러가지 기능을 제공합니다. (이 글에서는 다루지 않겠지만) 관심있는 분은 GNU C Library 매뉴얼을 찾아보기 바랍니다.
obstack에 관련된 것 중 추가적으로 알려드리고 싶은 것들입니다:
  • 조금씩 할당해 쓰는 방식을 쓸 때, 더욱 빠르게 쓸 수 있는 방법이 있습니다. "Extra Fast Growing Object"란 것인데, 이는 메모리를 얻어쓸 때, obstack이 내부적으로 할당한 메모리 블럭의 크기를 넘지 않는다는 확신이 있을 때 사용합니다. 내부적으로 할당한 메모리 크기는 obstack_room()으로 확인할 수 있습니다.
  • 일반적으로 obstack_init()을 호출하면, obstack은 먼저 커다란 메모리 블럭을 하나 할당하고 나서 시작합니다. 시스템에 따라 다르지만, 대개 이 크기는 4096 byte입니다. 만약, 이 초기 블럭의 크기가 너무 크다고 생각하면, (매뉴얼에는 나와 있지 않지만) obstack_init() 대신에 obstack_begin()을 써서, 초기 크기가 적은 obstack을 만들 수 있습니다. (자세한 것은 obstack의 소스를 참고하기 바랍니다)
  • obstack이, 내부적으로 메모리를 할당하다가 메모리 부족 현상이 발생하면 에러를 리턴하지 않고, 에러 처리 함수를 호출합니다. 이 함수를 바꾸려면, 전역 함수 포인터인 obstack_alloc_failed_handler를 적당하게 바꿔주면 됩니다. 물론 이 함수 포인터를 적절하게 바꿔서, obstack 관련 모든 함수가 에러가 발생할 경우, 에러를 리턴하는 방식으로 wrapper를 만들 수도 있습니다.

Beej's Guide to Unix IPC

Beej's Guide to Unix IPC

Brian "Beej Jorgensen" Hall
Version 1.1.2
December 15, 2010
Copyright © 2010 Brian "Beej Jorgensen" Hall

Contents


1. Intro
1.1. Audience
1.2. Platform and Compiler
1.3. Official Homepage
1.4. Email Policy
1.5. Mirroring
1.6. Note for Translators
1.7. Copyright and Distribution

2. fork() Primer
2.1. "Seek ye the Gorge of Eternal Peril"
2.2. "I'm mentally prepared! Give me The Button!"
2.3. Summary

3. Signals
3.1. Catching Signals for Fun and Profit!
3.2. The Handler is not Omnipotent
3.3. What about signal()
3.4. Some signals to make you popular
3.5. What I have Glossed Over

4. Pipes
4.1. "These pipes are clean!"
4.2. fork() and pipe()—you have the power!
4.3. The search for Pipe as we know it
4.4. Summary

5. FIFOs
5.1. A New FIFO is Born
5.2. Producers and Consumers
5.3. O_NDELAY! I'm UNSTOPPABLE!
5.4. Concluding Notes

6. File Locking
6.1. Setting a lock
6.2. Clearing a lock
6.3. A demo program
6.4. Summary

7. Message Queues
7.1. Where's my queue?
7.2. "Are you the Key Master?"
7.3. Sending to the queue
7.4. Receiving from the queue
7.5. Destroying a message queue
7.6. Sample programs, anyone?
7.7. Summary

8. Semaphores
8.1. Grabbing some semaphores
8.2. Controlling your semaphores with semctl()
8.3. semop(): Atomic power!
8.4. Destroying a semaphore
8.5. Sample programs
8.6. Summary

9. Shared Memory Segments
9.1. Creating the segment and connecting
9.2. Attach me—getting a pointer to the segment
9.3. Reading and Writing
9.4. Detaching from and deleting segments
9.5. Concurrency
9.6. Sample code

10. Memory Mapped Files
10.1. Mapmaker
10.2. Unmapping the file
10.3. Concurrency, again?!
10.4. A simple sample
10.5. Summary

11. Unix Sockets
11.1. Overview
11.2. What to do to be a Server
11.3. What to do to be a client
11.4. socketpair()—quick full-duplex pipes

12. More IPC Resources
12.1. Books
12.2. Other online documentation
12.3. Linux man pages

Zlib 활용

Zlib 활용

윤 상배

dreamyun@yahoo.co.kr

차례
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은 압축이 풀린 데이터의 변위를 가르킨다. whence는 lseek(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등의 압축명령을 주로 사용한다. 이들 내용에 대해서는 자세히 다루지 않을 것이다. 직접 구현해보는 것도 매우 재미있을 것이다.