이 글은 2008년도에 개정된 POSIX.1-2008 표준에 추가된 함수, dprintf에 대해 소개하는 글입니다.

TOC
1. 형식화된 입출력에서 printf 란?
2. printf와 고수준 파일 처리
3. 새로운 형식화된 출력 dprintf
4. 결론 


과거 타자기부터 컴퓨터까지 수 많은 발전 중에 가장 중요한 것중에 하나는 데이터를 정확하고 멋드러지게 출력하는 것이었다. 그러다보니 프로그래밍에서 빠질 없는 것이 바로 형식화된 입출력(Formatted Input/Output)이다. (형식화된 입출력이란 데이터의 형태를 변환하여 읽거나 출력하는 것으로 로케일 변환, 자릿수 맞춤, 들여쓰기(tab) 등등의 기능을 말한다.)

The Type Writer



1. 형식화된 입출력에서 printf 란?
형식화된 입출력에서 printf는 출력 쪽을 담당하는 함수이다. 표준 출력으로 내보내는 기초적인 printf는 fprintf, sprintf, snprintf, vsprintf 등등 많은 파생된 함수가 존재한다. C언어를 배운 사람 치고 printf를 모르는 사람은 없을 것이다. 왜냐하면 C언어 책에서 항상 거의 처음에 다루는 예제에 나타나기 때문이다.

그러면 의례히 C언어를 배울 때 처음 작성하는 Hello world라는 예제를 보도록 하자.  
#include <stdio.h>
#include <stdlib.h>
int main()
{
	printf("Hello tistory\n");
	return EXIT_SUCCESS;
}
여기서는 그래도 블로그를 제공한 티스토리를 위해 Hello tistory로 바꿔보았다. 예제 5번행의 printf는 화면에 출력을 하는데 사실상 형식화된 출력이라고 할만한 것이 없다. 그러나 좀 다른 예로 printf("name = %s, age = %d\n", s.name, s.age)라는 코드를 보면 %s와 %d 부분이 뒤의 argument에 의해서 바뀌는 것을 볼 수 있다. 이렇게 실시간으로 출력 형식이 바뀌는 것은 참 대단한 것이다. (물론 아무런 감흥 없이 쓰는 사람도 있겠지만...)

만일 printf를 사용하지 않고 printf("name = %s, age = %d\n", s.name, s.age)와 똑같은 기능을 구현한다고 해보자. 어떻게 해야 할까? 일반적인 경우라면 strcat이나 stpcpy 함수를 사용해서 문자열을 붙이게 될 것이다. 그리고 s.age는 숫자를 읽어서 문자로 변환한 뒤에 붙여야 한다. 말로는 쉽지만 이 과정이 보통 귀찮은게 아니다. 따라서 초기 C언어에서 printf가 가져온 변화는 실로 위대한 것이었다. 


2. printf와 고수준 파일 처리
printf 계열을 사용하여 파일에 출력하는 경우를 생각해보자. 위의 예제를 그대로 파일에 기록하는 것으로 바꿔보았다. (자질구레한 에러처리는 하지 않았다. 에러처리를 배우는 과정이 아니기 때문에...라고 하고 싶지만 사실은 귀찮았다.
#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE *fp;
	fp = fopen("hello.txt", "w"); /* 고수준 파일 처리 스트림 */
	fprintf(fp, "Hello tistory\n");
	fclose(fp);
	return EXIT_SUCCESS;
}
이 예제는 간단하게 hello.txt 파일을 열고 거기에 출력하도록 되어있다. printf 대신 fprintf를 사용하는 것만 다르다. 여기서 사용하는 FILE *fp 는 고수준 파일 처리(High level file handle)의 기능이다. 모든 C언어에서 사용가능한 표준이다.

하지만 유닉스 계열에서는 저수준(Low level I/O)의 입출력 함수도 제공한다. 바로 파일 기술자(file descriptor)를 이용하는 기능인데 출력 함수도 write, writev 같은 형태를 사용한다. 그러나 write 함수는 단지 버퍼의 내용을 파일로 출력하는 기능만 하고 형식화된 출력은 불가능했다.

그래서 유닉스에서 자유자재로 형식화된 출력을 쓰려면 C언어 표준의 고수준 파일 처리 스트림을 사용하거나 먼저 sprintf 계열로 버퍼에 출력한 뒤에 파일에 출력해야만 했다. 아래 그림은 형식화된 입출력을 고수준과 저수준으로 나눠서 보여주는 비교 예다.

High level I/O vs Low level I/OHigh level I/O vs Low level I/O



3. 새로운 형식화된 출력 dprintf 
그러나 sprintf와 write를 연달아 쓰는 것은 매우 불편했다. 그래서 2008년에 개정된 POSIX.1-2008 표준에서는 dprintf 함수를 추가하였고, 이 새로운 함수의 기능은 저수준 파일 처리에서도 형식화된 출력을 제공하는 것이었다. 따라서 위의 그림에서 sprintf와 write를 연달아 쓰던 옛날 방식은 이제 dprintf(fd, "counter : %d", i)의 한줄로 처리할 수 있게 된 것이다.

Synopsis
int dprintf(int
 fildes, const char *restrict format, ...);

The dprintf() function shall be equivalent to the fprintf() function, except that dprintf() shall write output to the file associated with the file descriptor specified by the fildes argument rather than place output on a stream. (SUSv4) 


dprintf는 2008년에도 추가된 표준이므로 2011년도의 임플리먼테이션은 대부분 dprintf를 지원하고 있다. 다만 몇몇 구형 유닉스에서는 지원되지 않을 수 있으므로 해당 시스템이 IEEE 1003.1-2008, susv4의 표준안을 지원하고 있는지부터 확인해보면 된다.(정확하게는 2006년도 Technical Standard 그룹에서 추가하였으므로 이후에 대부분의 C임플리먼테이션은 이를 서서히 추가하기 시작했다.)

4. 결론
이제 유닉스,리눅스 프로그래머들에게도 편리한 함수가 하나 더 생겼다. 그것은 바로 dprintf로서 파일 기술자에도 자유롭게 형식화된 출력을 하는 함수이다. 만일 아직도 이 기능을 몰라서 못 쓴다면 이제부터라도 써보자. 특히 로그파일이나 간단한 export file 만들때의 코드량이 많이 줄어들게 될 것이다.

* 참고 사이트
Open Group (http://www.opengroup.org) 



저작자 표시
신고

티스토리 툴바