Unix System Programming 9 - :namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />김성호(moohou) :namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
_________________________________________________________________________
제9장. 표준 I/O 라이브러리
9.1. 서론
우선 Dennis Ritche 가 최초로 개발하고, 모든 UNIX 시스템에서 제공되는 C
라이브러리의 중요한 부분인 표준 I/O 라이브러리를 살펴본다. 표준 I/O 룬틴
들은 UNIX 의 특정한 특성에 매어 있지 않고 C 언어를 위한 ANSI 표준의 일부
분이 된다는 의미에서 이식가능하다. 제대로 된 C 컴파일러라면 운영체제에 관
계없이 표준 I/O 라이브러리의 완전한 구현을 이용할 수 있게 할 것이다. 앞으
로 라이브러리의 UNIX 구현과 특성, 시스템 프로그래머와 소프트웨어 개발자를
위한 유용성에 대하여 살펴보자.
9.2. 스트림과 FILE 구조
표준 I/O 루틴들은 스트림이라고 하는 엔티티(entity)를 통해 화일에 접근한
다. 본질적으로 하나의 스트림은 프로그램과 개방 화일사이의 자료의 흐름이
다. 프로그램내에서 스트림은 FILE 형의 구조에 대한 포인터에 의해 식별된다.
FILE 의 정의는 표준 헤더 화일 stdio.h 에서 찾을 수 있다.
typedef struct _iobuf {
int _cnt;
unsigned char *_ptr;
unsigned char *_base;
char _flag;
char _file;
} FILE;
_file 은 char 로서 선언되었으나 실제로는 '작은' 한 바이트 정수로서 취급
된다. 이것은 FILE 구조에 의해 식별되는 스트림이 화일과 연결될때 화일 기술
어를 나타낼 것이다. 이것은 표준 I/O 가 궁극적으로 화일 접근 프리미티브
open, read, write, close 등을 사용한다는 사실을 명시적으로 나타내는 것이
다. 그것은 표준 I/O 라이브러리에 의한 내부적 사용을 위한 것이다. _flag 는
스트림을 위한 제어정보를 포함한다. 예를들어 이것은 표준 I/O 라이브러리가
해당 스트림이 쓰기, 읽기 또는 둘다를 위해 개방된 것인지를 나타낸다. _ptr,
_cnt 와 _base 는 개방 스트림과 관련된 문자 버퍼를 나타낸다. _base 는 버퍼
의 시작점 을 가리킨다. _ptr 은 처리를 위해 사용가능한 버퍼의 다음 문자를
가리키고, 정수 _cnt 는 _ptr 가 나타내는 위치 이후에 버퍼에 남아있는 문자
의 수를 나타낸다. 버퍼의 크기는 BUFSIZ 바이트가 된다. BUFSIZ 자체는
stdio.h 에 정의 되어 있고, 2 장에서 보았듯이 호스트환경을 위한 디스크 블
럭킹 요소를 정해준다. 전형적인 값은 512 와 1024 등이다.
9.3. 스트림을 열고 닫기 : fopen 과 fclose
#include <stdio.h>
FILE *stream;
char *filename, *type;
int retval;
.
.
stream = fopen(filename, type);
retval = fclose(stream);
fopen 과 fclose 는 open 과 close 에 해당하는 표준 I/O 라이브러리이다.
fopen 루틴은 filename 에 의해 식별되는 화일을 개방하고, 하나의 filename
과 관련되게 한다. 성공적으로 수행되면 fopen 은 개방스트림을 식별하기 위한
FILE 구조에 대한 포인터를 돌려준다. fclose 는 화일과 stream 에 의해 식별
된 스트림을 닫고, 스트림이 출력 스트림이면 그 스트림의 버퍼에 남아있는 모
든 자료를 출력한다. fopen 의 두번째 인수는 접근의 모드를 결정하는 문자열
이다. 이것은 다음의 기본적인 값을 가질 수 있다.
r fopen 은 읽기 전용으로 개방한다.(만약 화일이 존재하지 않으면 fopen
호출은 실패하고 NULL 을 돌려준다.)
w filename 을 생성하거나 절단(truncate)하고, 쓰기 전용으로 개방한다.
a 읽기 전용으로 filename 을 개방하고 쓰여지는 모든 자료는 자동적으로
그 화일의 끝에 첨가된다. 화일이 존재하지 않으면 쓰기를 위해 화일을
생성한다.
하나의 화일은 갱신을 위해 개방될 수도 있다. 이것은 그 화일에 쓰기와 모두
할 수 있다는 것을 의미한다. 갱신모드는 open 에 전달되는 type 매개 변수에
추가적인 '+' 기호를 사용하여 나타내어진다.
r+ 갱신을 위해 filename 을 개방한다. 그 화일이 존재하지 않으면 fopen 은
실패한다.
w+ filename 을 생성 또는 절단한다. 그리고 그 화일을 갱신을 위해 개방한
다.
a+ 갱신을 위해 개방한다. 자료는 화일의 끝에 첨가된다. 횬舅 존재하지 않
으면 화일은 쓰기를 위해 생성된다.
9.4. 단일문자 I/O : getc 와 putc
#include <stdio.h>
FILE *inf, *outf;
int c;
.
.
c = getc(inf);
putc(c, outf);
표준 I/O 라이브러리에 의해 제공되는 가장 간단한 입력과 출력 루틴은 getc
와 putc 이다. getc 루틴은 입력 스트림 inf 로 부터 다음 문자(좀더 적당하게
말하면 다음 바이트)를 돌려준다. putc 는 outf 스트림에 하나의 문자를 출력
한다.
9.5. 스트림에 문자들을 되돌리기 : ungetc
#include <stdio.h>
FILE *stream;
int c, retval;
.
.
retval = ungetc(c, stream);
ungetc 는 입력 스트림에 문자 c 를 되돌려 준다. 이것은 논리적인 연산이고
입력화일 자체 는 변경되지 않는다. 한번에 오직 하나의 문자 만을 되돌릴 수
있다.
9.6. 표준 입력, 표준 출력 과 표준 오류
표준 I/O 라이브러리는 표준 입력, 표준 출력과 표준 오류에 연결된 세가지
스트림을 제공한다. 이러한 표준 스트림들은 개방될 필요가 없다. 다음의 FILE
포인터에 의해 식별된다.
stdin 표준입력의 FILE 포인터
stdout 표준출력의 FILE 포인터
stderr 표준오류의 FILE 포인터
다음 문장은 stdin 에서 다음 문자를 가져온다.
inchar = getc(stdin);
stdin 과 stdout 은 아주 자주 사용되기 때문에 getc 와 putc 의 축약형인
getchar 와 putchar 이 제공된다. getchar 는 stdin 으로 부터 다음 문자를 읽
어오고 putchar 는 stdout 으로 하나의 문자를 보낸다. 함수 getchar 와
putchar 는 매개 변수로서 FILE 포인터를 갖지 않는다.
9.7. 표준 I/O 상태 루틴
스트림으 상태를 알아보기 위한 루틴이 몇가지 있다.
#include <stdio.h>
int retval, fd;
FILE *stream;
.
.
retval = ferror(stream);
retval = feof(stream);
clearerr(stream);
fd = fileno(steam);
ferror 는 이전의 입력 또는 출력 요구 때문에 스트림에 오류가 발생했다면 0
이 아닌 값을 돌려주는 부울(boolean)함수이다.
9.8. 라인 단위 입력과 출력
자료를 라인 단위로 입력하고 출력하기 위한 루틴들은 단일문자 I/O 루틴들과
밀접하게 연관 되어 있다. 기본적인 라인 입력 루틴은 gets 와 fgets 이다.
#include <stdio.h>
char *buf, *retstring;
FILE *inf;
int nsize;
.
.
retstring = gets(buf);
retstring = fgets(buf, nsize, inf);
gets 는 표준 입력 스트림 stdin 으로 부터 일련의 문자들을 읽고 각 문자를
buf 가 가리키는 버퍼에 저장한다. 문자들은 화일의 끝 또는 newline 을 만날
때까지 읽혀지고, newline 은 무시되며 스트링을 형성하기위해 널 문자를 buf
에 넣는다. 오류가 발생하거나 화일의 끝에 도달하고 아무런 문자도 읽혀지지
않으면, NULL 이 돌아온다. fgets 는 gets 의 일반화된 형태이다.
gets 와 fgets 에 반대되는 루틴들은 각각 puts 와 fputs 이다.
#include <stdio.h>
char *string;
FILE *outf;
int retval;
.
.
retval = puts(string);
retval = fputs(string, outf);
puts 는 널 문자를 제외하고 표준 출력 스트림으로 string 에 있는 문자들을
출력한다. fputs 는 outf 가 나타내는 출력 스트림에 string 을 출력한다. 시
스템의 오래된 버전과의 호환성을 보장하기 위해 puts 는 fputs 와는 다르게
개행 문자를 첨가한다. 오류의 경우 두 함수는 EOF 를 돌려준다.
9.9. 이진 입력과 출력 : fread 와 fwrite
#include <stdio.h>
char *buffer;
int size, nitems, result;
FILE *inf, *outf;
.
.
result = fread(buffer, size, nitems, inf);
result = fwrite(buffer, size, nitems, outf
);
이진 입력과 출력을 위해 아주 유용한 두개의 루틴이 제공된다. fread 는 inf
에 해당하는 입력 스트림으로부터 nitems 개의 자료를 읽어 들인다. 읽혀지는
바이트들은 문자형 배열 buffer 에 놓여진다. 읽혀진 각 바이트 길이가 size 인
바이트 열로서 나타내어진다. 복귀값 resul 는 성공적으로 읽혀진 자료의 갯수
를 나타낸다. fwrite 는 fread 의 반대이다.
9.10. 무작위화일 접근 : fseek, rewind, ftell
표준 I/O 라이브러리는 프로그래머가 스트림의 화일 포인터 위치를 재조정하
거나 화일 포인터 의 현재 위치를 찾을 수 있도록 하는 무작위 접근 루틴을 제
공한다. 이러한 루틴들은 fseek, rewind 와 ftell 이다.
#include <stdio.h>
FILE *stream;
long offset, position;
int direction, result;
.
.
result = fseek(stream, offset, direction);
rewind(stream);
position = ftell(stream);
fseek 은 하위 수준의 lseek 과 유사하고 스트림과 관련된 화일의 화일 포인
터의 위치를 지정 한다. 그러므로 fseek 은 다음의 입력 또는 출력의 위치를
다시 정의한다. direction 매개변수는 화일에서 새로운 위치가 어디에서부터
계산되는가를 결정한다. direction 이 0 으로 주어지면 화일의 처음에서 부터
새로운 위치를 계산하고, 그 값이 1 이면 현재의 위치가 사용되며, 값이 2 로
주어지면 화일의 끝에서 부터 새로운 위치를 계산한다. offset 매개변수는 이
시작 위치에 더해질 바이트의 수를 나타낸다.rewind(stream) 은 fseek(stream,
0, 0) 의 축약형이다. 즉, rewind 는 읽기-쓰기 포인터를 화일의 처음으로 지
정한다. rewind 는 아무런 값도 돌려주지 않는다(실제로, rewind 는 void 함수
로 정의된다). ftell 은 스트림에서 프로그램의 현재 위치를 돌려준다. 현재의
위치는 화일의 처음부터 (0 에서 부터 계산)의 바이트 수로 주어진다.
9.11. 형식화된 출력 : printf
#include <stdio.h>
char *fmt, *string;
FILE *outf;
int retval;
/*NB parameters arg1 .. have arbitrary
type */
.
.
retval = printf(fmt, arg1, arg2 ... argn);
retval = fprintf(outf, fmt, arg1, arg2 ..
argn);
retval = sprintf(string, fmt, arg1, arg2 .
argn);
이 루틴들은 각각 출력 문자열을 생성하기 위해 임의의 유형의 가변적인 갯수
의 매개변수(arg1, arg2 등)와 형식 지정 문자열 fmt 를 매개변수로 갖는다.
이 출력 문자열은 fmt 에 지정된 형식을 이용하 여 매개변수 arg1 부터 argn
까지의 정보를 표현한다.
정수형 변환(integer conversion)
%d 부호가 있는 정수를 위한 표준변환코드이다. 정수가 음수이면 부호가 자
동적으로 첨가된다.
%u 매개변수는 십진수 형식으로 출력되는 부호없는 정수이다.
%o 매개변수는 부호없는 팔진수 형식으로 출력되는 정수이다.
%x 매개변수는 부호없는 십육진수 형식으로 출력되는 정수이다. 문자 a, b,
c, d, e, f 가 부가적인 십육진수 숫자를 나타내기 위해 사용된다. 변환
명세가 %X 로 주어지면 A, B, C, D, E, F 가 사용된다.
%ld 매개변수는 부호있는 long 정수이고 십진수 형식으로 출력된다. 프로그
래머는 %lo, %lu, %lx 와 %lX 을 사용할 수 있다.
부동 소숫점 변환(floating-point conversion)
%f 매개변수는 십진수 형식으로 출력되는 float 또는 double 형이다.
%e 매개변수는 지수형식으로 출력되는 float 또는 double 형이다. 이것은 과
학 응용에 잘 사용된다.
%g 이것은 %e 와 %f 의 혼합이다. 이것은 해당하는 매개변수가 float,
double 중 하나라는 것을 나타낸다.
문자열과 문자제어
%c 매개변수는 문자 그대로 출력되는 char 형 이다. 문자에 저장된 숫자값은
정수 변환코드를 사용하여 출력할 수 있다.
%s 해당하는 매개변수는 문자열(즉, 문자형 포인터)로서 취급된다. 이 문자
열 내용이 출력 스트림으로 전달된다. 물론, 문자열은 널 문자로 끝나야
한다.
길이와 정도(precision)의 명시
변환 명세는 매개변수가 출력되는 필드의 최소 문자 길이에 관한 정보와 그
필드는 정확도에 대한 정보를 포함할 수 있다. 정수형 매개변수 의 경우정도는
최소 갯수의 숫자를 나타내고, float 또는 double 매개변수에서는 소수점 다음
에 나타날 숫자의 갯수를 나타낸다. 문자열 매개변수에서는 그 문자열에서 취
할 수 있는 문자들의 최대 갯수를 나타낸다.
%10.5d
는 해당 정수 매개변수를 10 문자 길이로 출력 하는 것을 의미한다.
%.5f
는 해당하는 float 또는 double 매개변수를 5 자리 소수점으로 출력한다.
%10s
는 길이가 최소 10 문자인 필드로 해당 문장열을 출력한다.
%-30s
는 해당 문자열 매개변수가 30 문자의 필드에서 왼쪽부터 출력되는 것을 나
타낸다.
특수부호
출력 변환 명세는 추가적인 부호에 의해 더욱 복잡해 질수 있다. # (hash 또
는 sharp)이 그 예이다. 이것은 명세의 길이 부분 바로 앞에 나타나야 한다.
int arg;
arg = 0xFF;
printf("In octal, %#o\n", arg);
는 다음을 출력한다.
In octal, 0377
+ 부호는 숫자가 양수일 때 + 부호가 출력되도록 한다.
float farg;
farg = 57.88;
printf("Value of farg is %-+10.f\n");
은
Value of farg is +57.88
을 출력한다.
sprintf 루틴
sprintf 을 출력 루틴으로 생각하지 마라. sprintf 는 가장 신축성 있는 문
자열 조작과 C 라이브러리에서 일반적인 변환 능력을 제공한다.
/*genkey -- generate key for use in data-
base */
/* key will always be 20 chars
long */
#include <stdio.h>
char *genkey(buf, suppcode, orderno)
char *buf;
char *suppcode;
long orderno;
{
if(strlen(suppcode) != 10)
return (NULL);
sprintf(buf, "%s_%.9ld", suppcode,
orderno);
return(buf);
}
genkey 의 다음 호출은
printf("%s\n", genkey(buf, "abcedfghij",
12));
다음 문자열을 출력한다.
abcedfghij_000000012
9.12. 형식화된 입력 : scanf
#include <stdio.h>
char *fmt, *string;
FILE *inf;
int retval;
/*NB: ptr1... ptrn are all pointers.
*The type of the variable they point
*to is arbitrary.
*/
.
.
retval = scanf(fmt, ptr1, ptr2, .. ptrn);
retval = fscanf(inf, fmt, ptr1, ptr2 ..
ptrn);
retval = sscanf(string, fmt, ptr1, ptr2
.. ptrn);
이들 루틴들은 printf 의 루틴들과는 반대되는 것이다. 이들은 모두 스트림으
로부터 입력(sscanf 의 경우는 문자열)을 받아 문자얼 fmt 의 형식 지정에 따
라 입력을 해독하고 결과로 얻어진 자료를 포인터 ptr1, ..., ptrn 이 나타내
는 변수에 지정한다. 스트림에 대한 화일 포인터는 처리된 문자의 갯수만 전진
한다. 일반적으로 scanf 형식지정 문자열은 다음을 포함한다.
1. 공백문자(white-space character) : 공백, 탭, 개행문자, form feed
2. 보통의 비공백문자 : 이것은 입력스트림의 해당문자와 그대로 일치된다.
3. 변환명세 : printf 에 사용되는 명세와 동일하다.
#include <stdio.h>
main()
{
int i1, i2;
float flt;
char str1[10], str2[10];
scanf("%2d%2d %f %s %s", &i1, &i2,
&flt, str1, str2);
.
.
}
9.13. 표준 I/O 라이브러리를 사용한 프로그램의 수행
표준 I/O 라이브러리에는 프로그램이 다른 프로그램을 수행할 수 있게 해 주
는 루틴이 몇개 있다. 이들 중에서 가장 기초적인 것은 system 이다.
#include <stdio.h>
int retval;
char *comstring;
.
.
retval = system(comstring);
시스템은 comstring 에 담겨진 명령어를 수행한다. 이를 위해서 우선 자식 프
로세스 생성한다. 자식 프로세스는 다시 exec 를 호출하여 표준 UNIX 쉘
(/bin/sh)을 수행하고 comstring 을 입력으로 준다. (SVID 에서 실제로 언급한
것은 명령어 해석기이지만 이도 역시 UNIX 쉘과 유사하게 해동해야 한다.)
system 루틴에서 부모 프로세서 wait 를 호출함으로써 주어진 명령어의 수행이
완료된 다음에 수행을 재개하도록 한다. 결과적으로 돌아오는 값 retval 은 쉘
의 종료상태로서, 이 값에 의해 주어진 명령어가 성공했는지 실패했는 지를 알
아낼수 있다. 만약 fork 나 exec 호출이 실패하면 retval 의 값은 -1 이 된다.
system 에는 커다란 결점이 있다. 프로그램은 자신이 수행한 명령어로 부터의
출력에 직접 접근할 길이 없다. 이접을 해결하려면 표준 I/O 라이브러리에 있
는 popen 과 pclose 루틴 을 사용해야 한다.
#include <stdio.h>
FILE *strm, *popen();
char *comstring, *typestring;
int retval;
.
.
strm = popen(comstring, type);
retval = pclose(strm);
system 의 경우와 같이 popen 도 자식 프로세스를 생성하여 comstring 이 가
리키는 명령어를 수행게 한다. 그러나 system 과 다른 점은 호 출 프로세스와
명령어 사이에 파이프를 생성하는 것이다. 이때 파이프에 스트림이 대응되게
되고, 이는 strm 에 지정된다. 이때 type 의 값이 "w" 라면 프로그램에서의 출
력이 스트림을 통해 명령어의 표준 입력으로 전달될 수 있다. type 이 "r" 이
라면 반대로 명령어의 표준 입력을 프로그램에서 받아들일 수 있게 된다.
popen 으로 개방된 스트림은 항상 pclose 로 폐쇄되어야 한다.
9.14. 기타의 호출
표준 I/O 라이브러리에 있는 그 밖의 함수를 살펴보자.
9.14.1. freopen 과 fdopen
#include <stdio.h>
FILE *oldstream, *newstream;
char *type, *filename;
int filedes;
.
.
newstream = freopen(filename, type, oldstre
am);
oldstream = fdopen(filedes, type);
freopen 은 oldstream 이 가리키는 스트림을 폐쇄하고 filename 에서오는 입
력을 위해 이를 다시 개방한다. type 은 새로운 스트림을 접근하는 모드를 결
정한다. 두 함수 모두 오류가 발생하면 NULL 을 돌려준다.
9.14.2. 단어 입출력 : getw 와 putw
#include <stdio.h>
int word, res;
FILE *inf, *outf;
.
.
word = getw(inf);
res = putw(word, outf);
이들 두 루틴은 단어(word) 단위로 입출력을 수행한다. 단어란 C 컴파일러가
제공하는 정수 형자료로서 단어의 크기, 그리고 이들 함수의 사용법은 컴퓨터
마다 다르다. getw 는 inf 가 가리키는 스트림의 다음번 단어 (즉 이진 정수)
를 가져온다. putw 는 outf 가 가리키는 스트림에 한 단어를 써 넣는다. 두 함
수 모두 오류 발생시에는 EOF 를 돌려준다.
9.14.3. 버퍼의 제어 : setbuf setvbuf
#include <stdio.h>
FILE *stream;
char buf1[BUFSIZ], buf2[SOMEVALUE];
int type, size, res;
.
.
setbuf(stream, buf);
res = setvbuf(stream, buf2, type, size);
setbuf 는 표준 I/O 라이브러리가 보통때 할당하는 버퍼 대신 buf1 을 사용
하도록 해준다. buf1 의 크기는 stdio.h 에 정의된 상수 BUFSIZ 가 결정한다.
setbuf 의 인수로 문자형 포인 NULL 을 전달하면 입출력은 버퍼에 담기지 않는
다. 이 기능은 비정상적으로 종료하는 프로그램에서 버퍼에 있는 자료를 잃어
버릴 우려가 있을 때, 이를 디버깅하기 위해 사용될 수 있다. setvbuf 는 표준
I/O 라이브러리에 새로 첨가된 함수로서 setbuf 보다 더 정밀한 제어를 가능하
게 한다. type 인수는 stream 이 버퍼링되는 방법을 지정한다. 이에 사용할
수 있는 값은 stdio.h 에 다음과 같이 세가지로 정의되어 있다.
_IOFBF
스트림은 완전히 버퍼를 사용할 수 있다. 이는 단말기에 연결되지 않은 모
든 스트림에 대한 기본 값이다. 따라서 자료를 읽고 쓸때는 효율을 극대화
하기 위해 BUFSIZ 개의 바이트를 단위로 한다.
_IOLBF
출력은 라인 단위로 버퍼에 담기고 newline문자가 쓰일 때마다 버퍼를 비우
게 된다. 버퍼를 비울 경우는 이 밖에도 버퍼가 모두 찼을 때나 입력이 요
구된 경우이다. 이는 단말기를 위한 기본값이며 대화식 사용에 도움을 주기
위해 고안되었다.
_IOBNF
이때는 입출력에 버퍼를 사용하지 않게 된다. 이 경우 buf2 와 size 는 무
시된다. 이 모드는 오류기록시 유용하다.
type 이나 size 에 부당한 값이 사용되면 setv buf 는 0 이 아닌 값을 돌려줌
에 주의하라. 역으로 0 이 돌아오면 성공을 의미한다.'Academy I > Tech Academy' 카테고리의 다른 글
VirtualBox의 Linux(CentOS)에서 공유폴더 설정 (0) | 2014.12.22 |
---|---|
Linux gdb 사용법 [I] (0) | 2014.12.19 |
Linux make 사용법 [I] (0) | 2014.12.18 |
Unix System Programming 10 (0) | 2014.12.16 |
Unix System Programming 8 (0) | 2014.12.16 |
Unix System Programming 7 (0) | 2014.12.16 |
Unix System Programming 6 (0) | 2014.12.16 |
Unix System Programming 5 (0) | 2014.12.16 |