티스토리 뷰
☞ printf, fprintf, sprintf
<stdio.h>
int printf(const char *template, ...);
int fprintf(FILE *stream, const char *template, ...);
int sprintf(char *s, const char *template, ...);
C언어를 배우면서 맨 처음 만나는 함수중에 하나가 바로 printf()일것입니다. C언어를 대표하는 함수지요.
그런데 이 printf() 함수도 스트림과 연관성을 갖습니다. 일정한 형식에 맞추어 스트림에 출력을 내보낼때 사용하는 함수입니다.
표준 출력(화면)에 출력을 내보낼때는 printf()를 사용하고 사용자가 지정하는 스트림으로 형식화된 출력을 내보낼때는 fprintf()를 사용합니다.
그 출력 방향이 메모리 스트림인 경우에는 sprintf()를 사용합니다. sprintf()를 사용하면 형식화된 출력을 문자열에 받을 수 있습니다.
세가지 함수 모두 공통적으로 출력 형식을 가지고 있는 문자열 template과 그 형식에 맞는 인수들을 전달해야 하는데 0~N 개의 인수들을 대표하는 것이 ...입니다.
C언어에서 ...는 문법적 요소로 함수 프로토타입에서 가변적인 개수의 인수를 나타냅니다. ...가 나타내는 여러 인수는 template으로 지정하는 형식에 맞게 순서와 개수, 데이터 타입에 맞게 전달해야 합니다.
각 함수들은 출력한 문자의 개수(길이)를 리턴하고 동작중 오류가 발생하면 음수를 리턴합니다.
☞ 예제1
void broker_config_dump (FILE * fp, const T_BROKER_INFO * br_info, int num_broker, int br_shm_id) { int i; const char *tmp_str; if (br_info == NULL || num_broker <= 0 || num_broker > MAX_BROKER_NUM || br_shm_id <= 0) return; fprintf (fp, "#\n# cubrid_broker.conf\n#\n\n"); fprintf (fp, "# broker parameters were loaded from the files\n"); for (i = 0; i < MAX_NUM_OF_CONF_FILE_LOADED; i++) { if (conf_file_loaded[i] != NULL) { fprintf (fp, "# %s\n", conf_file_loaded[i]); } } fprintf (fp, "\n# broker parameters\n"); fprintf (fp, "[broker]\n"); fprintf (fp, "MASTER_SHM_ID\t=%x\n\n", br_shm_id); for (i = 0; i < num_broker; i++) { fprintf (fp, "[%%%s]\n", br_info[i].name); ......
위의 예제 코드는 큐브리드 DBMS의 일부로 다양한 출력 형식 사용을 참조해 볼 수 있습니다. fprintf (fp, "#\n# cubrid_broker.conf\n#\n\n");는 단순 출력 스트링으로 ...에 해당하는 인수가 없습니다. 하나의 스트링에 여러개의 줄바꿈 문자를 사용해서 여러 줄에 걸친 형식 출력을 수행하고 있음을 참조할 수 있습니다. fprintf (fp, "MASTER_SHM_ID\t=%x\n\n", br_shm_id);은 하나의 정수형 변수를 받도록 %x를 지정했습니다. %문자로 ...에 인수를 지정하고 있음을 알 수 있습니다. fprintf (fp, "[%%%s]\n", br_info[i].name);에서는 %를 출력하기 위해서 %%로 겹쳐서 사용했고 %s로 문자열 포인터를 가진 인수를 지정하고 있습니다.
☞ 형식화된 출력에 사용할 수 있는 문자 상수
- \\ : 백슬래시(원화 표시)
- \" : 큰따옴표. 문자열내에서 작음따옴표(')는 그냥 기술해도 되지만 큰 따옴표는 \를 앞에 붙여야 함.
- \n : 줄바꿈
- \t : 탭문자
☞ 예제2
printf("\n%d,%i,%o,%u,%x,%X,%f,%e,%E,%g,%G,%a,%A,%c,%s,%p,%%", 10, 20, 0345, 45600, 0x12ff, 0x12ff, 3.1415926568, 3.1415926568, 3.1415926568, 3.1415926568, 3.1415926568, 3.1415926568, 3.1415926568, 'Z',"String", "String");
10,20,345,45600,12ff,12FF,3.141593,3.141593e+000,3.141593E+000,3.14159,3.14159,0x1.921fb5p+1,0X1.921FB5P+1,Z,String,00BB579C,%
위의 예제 코드와 결과는 printf() 함수에서 사용할 수 있는 변환 형태를 나열한 것입니다. 지정한 타입에 맞는 인수들을 순서에 맞게 전달해야 하는데 형식에서 지정한 것보다 인수가 많으면 나머지는 그냥 무시되지만 형식에서 지정한 것보다 인수가 적거나 잘못된 인수를 전달하면 예상치 못한 결과를 초래할 수 있습니다. 예를 들어 가장 많이 실수하거나 심각한 문제를 일으키는 것은 형식에서 %s를 기술하여 문자열 포인터를 인수로 전달해야 하는데 널 포인터가 전달되었거나 스트링이 아닌 엉뚱한 위치가 전달되는 경우 등입니다. 위의 예제는 %+문자로 가장 기본적인 변환형태를 기술했지만 %+문자 사이에 다양한 변환자와 플래그를 기술해서 좀더 자세한 변환 작업을 지시할 수 있습니다. 아래에서는 데이터 타입별 변환 형태를 자세하게 다룹니다.
☞ 정수 변환
- %d : 부호있는 정수 변환
- %i : 부호있는 정수 변환
- %u : 부호없는 정수 변환
- %o : 부호없는 8진수 정수 변환
- %x, %X : 부호없는 16진수 정수 변환
"%플래그 폭 최소길이 타입변경자 타입"의 형식에서 플래그는 % 바로 다음에 기술하여 정렬, 부호표시 등을 제어 합니다.
- - : 좌측 정렬 지시(정수 기본은 우측 정렬)
- + : 부호 강제 표시(정수 기본은 양수는 표시 하지 않음). 부호있는 정수타입인 %d, %i만 적용됨.
- : 공백 문자는 음수만 부호 표시 양수면 공백 표시. 부호있는 정수타입인 %d, %i만 적용됨.
- # : %o, %x, %X만 적용되는 것으로 C언어 8진수 및 16진수 상수 표시인 0 또는 0x, 0X를 앞에 붙임.
- 0 : 빈자리를 0으로 채웁니다. -로 좌측 정렬이면 무시됨. 부호 및 진수 표시 다음부터 0으로 채웁니다
최소길이는 "%5.0d" 처럼 소수점 표시로 보이지만 정수 변환에서는 소수점 크기가 아니라 표시할 최소길이입니다. "%5.0d"는 폭을 5자리로 하고 0이면 표시하지 않아도 된다는 의미입니다. "%.2d"는 전체 폭은 지정하지 않고 최소 길이는 2이상으로 표시하라는 의미로 0이면 "00"을 표시합니다.
타입변경자는 정수 타입 "d, i, u, o, x, X" 직전에서 상세한 타입을 지정하는 역할을 합니다. 사용자가 printf()인수로 int 보다 크기가 작은 char, short 타입을 전달하면 int로 자동 변환해서 전달합니다.
- h : short int 또는 unsigned short int 취급 지시
- l : long int 또는 unsigned long int 취급 지시.
printf("|%%5d|%%-5d|%%+5d|%%+-5d|%% 5d|%%05d|%%5.0d|%%5.2d|%%d|\n"); printf("|%5d|%-5d|%+5d|%+-5d|% 5d|%05d|%5.0d|%5.2d|%d|\n", 0,0,0,0,0,0,0,0,0); printf("|%5d|%-5d|%+5d|%+-5d|% 5d|%05d|%5.0d|%5.2d|%d|\n", 3,3,3,3,3,3,3,3,3); printf("|%5d|%-5d|%+5d|%+-5d|% 5d|%05d|%5.0d|%5.2d|%d|\n", -5,-5,-5,-5,-5,-5,-5,-5,-5); printf("|%5d|%-5d|%+5d|%+-5d|% 5d|%05d|%5.0d|%5.2d|%d|\n", 999999,999999,999999,999999,999999,999999,999999,999999,999999); printf("|%%5d|%%5u|%%5o|%%5x|%%5X|%%#5o|%%#5x|%%#5X|%%#10.8x|\n"); printf("|%5d|%5u|%5o|%5x|%5X|%#5o|%#5x|%#5X|%#10.8x|\n", 0,0,0,0,0,0,0,0,0); printf("|%5d|%5u|%5o|%5x|%5X|%#5o|%#5x|%#5X|%#10.8x|\n", 3,3,3,3,3,3,3,3,3); printf("|%5d|%5u|%5o|%5x|%5X|%#5o|%#5x|%#5X|%#10.8x|\n", -5,-5,-5,-5,-5,-5,-5,-5,-5); printf("|%5d|%5u|%5o|%5x|%5X|%#5o|%#5x|%#5X|%#10.8x|\n", 999999,999999,999999,999999,999999,999999,999999,999999,999999);
|%5d|%-5d|%+5d|%+-5d|% 5d|%05d|%5.0d|%5.2d|%d| | 0|0 | +0|+0 | 0|00000| | 00|0| | 3|3 | +3|+3 | 3|00003| 3| 03|3| | -5|-5 | -5|-5 | -5|-0005| -5| -05|-5| |999999|999999|+999999|+999999| 999999|999999|999999|999999|999999| |%5d|%5u|%5o|%5x|%5X|%#5o|%#5x|%#5X|%#10.8x| | 0| 0| 0| 0| 0| 0| 0| 0| 00000000| | 3| 3| 3| 3| 3| 03| 0x3| 0X3|0x00000003| | -5|4294967291|37777777773|fffffffb|FFFFFFFB|037777777773|0xfffffffb|0XFFFFFFFB|0xfffffffb| |999999|999999|3641077|f423f|F423F|03641077|0xf423f|0XF423F|0x000f423f|
위의 예제를 보면 폭은 부호를 포함한 길이이고 표시할 데이터가 지정한 폭을 넘어서더라도 자르지 않고 모두 표시하는 것을 알 수 있습니다. - 플래그로 좌측 정렬되는 것과 최소길이가 0이면 0은 표시되는 내용이 없음을 알 수 있습니다. 부호 없는 정수를 변환하는 "u, o, x, X"의 경우에는 printf()로 음수값을 전달해도 부호없는 정수로 취급함을 알 수 있습니다.
☞ 실수 변환
- %f : 실수 변환
- %e, %E : 지수형 실수 변환
- %g, %G : 실수/지수형 실수 자동 변환
- %a, %A : 자료 교환용 실수 변환
"%플래그 폭 정밀도 타입변경자 타입"의 형식에서 플래그는 % 바로 다음에 기술하여 정렬, 부호표시 등을 제어 합니다.
- - : 좌측 정렬 지시(기본은 우측 정렬)
- + : 부호 강제 표시(기본은 양수는 표시 하지 않음).
- : 공백 문자는 음수만 부호 표시 양수면 공백 표시.
- # : 소수점 강제 표시. 소수점이후에 표시할 숫자가 없어도 표시
- 0 : 빈자리를 0으로 채웁니다. -로 좌측 정렬이면 무시됨. 부호 표시 다음부터 0으로 채웁니다
정밀도를 지정하지 않으면 기본값은 6이고 "f, e, E, a, A" 타입은 정밀도를 소수점으로 처리해서 명시적으로 0으로 지정하면 소수점을 표시하지 않습니다. "g, G" 타입의 경우에는 정밀도를 표시할 유효숫자의 개수로 인식하기 때문에 명시적으로 0을 지정하거나 정밀도를 표시하지 않으면 1로 취급합니다. 실수 변환의 경우 printf()를 호출할 때 float타입을 인수로 전달해도 double로 자동 변환해서 전달하는데 long double을 전달하고 인수를 long double로 취급 지시 하려면 타입 변경자로 'L'을 사용하면 됩니다.
printf("|%%-10.3f|%%+10.3f|%%#010.0f|%%10.3e|-%%10.3e|%%10.3G|%% 10.3G|%%+10.3a|%%10.3A\n"); printf("|%-10.3f|%+10.3f|%#010.0f|%10.3e|-%10.3e|%10.3G|% 10.3G|%+10.3a|%10.3A\n", 0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0); printf("|%-10.3f|%+10.3f|%#010.0f|%10.3e|-%10.3e|%10.3G|% 10.3G|%+10.3a|%10.3A\n", 3,3,3,3,3,3,3,3,3); printf("|%-10.3f|%+10.3f|%#010.0f|%10.3e|-%10.3e|%10.3G|% 10.3G|%+10.3a|%10.3A\n", 3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0); printf("|%-10.3f|%+10.3f|%#010.0f|%10.3e|-%10.3e|%10.3G|% 10.3G|%+10.3a|%10.3A\n", -5.0,-5.0,-5.0,-5.0,-5.0,-5.0,-5.0,-5.0,-5.0); printf("|%-10.3f|%+10.3f|%#010.0f|%10.3e|-%10.3e|%10.3G|% 10.3G|%+10.3a|%10.3A\n", 23.1234,23.1234,23.1234,23.1234,23.1234,23.1234,23.1234,23.1234,23.1234); printf("|%-10.3f|%+10.3f|%#010.0f|%10.3e|-%10.3e|%10.3G|% 10.3G|%+10.3a|%10.3A\n", 999999.0,999999.0,999999.0,999999.0,999999.0,999999.0,999999.0,999999.0,999999.0); printf("|%-10.3f|%+10.3f|%#010.0f|%10.3e|-%10.3e|%10.3G|% 10.3G|%+10.3a|%10.3A\n", 98765.4321012,98765.4321012,98765.4321012,98765.4321012,98765.4321012,98765.4321012,98765.4321012,98765.4321012,98765.4321012);
|%-10.3f|%+10.3f|%#010.0f|%10.3e|-%10.3e|%10.3G|% 10.3G|%+10.3a|%10.3A |0.000 | +0.000|000000000.|0.000e+000|-0.000e+000| 0| 0|+0x0.000p+0|0X0.000P+0 |0.000 | +0.000|000000000.|6.366e-314|-1.482e-323| 1.#R|-9.26E+061|-0x1.ccdp+205|-0X1.CCDP+205 |3.000 | +3.000|000000003.|3.000e+000|-3.000e+000| 3| 3|+0x1.800p+1|0X1.800P+1 |-5.000 | -5.000|-00000005.|-5.000e+000|--5.000e+000| -5| -5|-0x1.400p+2|-0X1.400P+2 |23.123 | +23.123|000000023.|2.312e+001|-2.312e+001| 23.1| 23.1|+0x1.720p+4|0X1.720P+4 |999999.000|+999999.000|000999999.|1.000e+006|-1.000e+006| 1E+006| 1E+006|+0x1.e84p+19|0X1.E84P+19 |98765.432 |+98765.432|000098765.|9.877e+004|-9.877e+004| 9.88E+004| 9.88E+004|+0x1.81dp+16|0X1.81DP+16
위의 예제를 보면 형식으로는 실수를 지정했는데 인수로 정수를 전달하면 예상치 않은 결과가 나온 것을 확인할 수 있습니다. 정수 3을 전달한 것과 실수 3.0을 전달한 것은 printf() 입장에서 큰 차이가 납니다. 정수는 4바이트를 전달할 것이고 실수 8바이트를 기대할 것이기 때문입니다. 형식에 맞도록 정확한 타입을 인수로 전달해야 합니다.
☞ 기타 변환
- %c : 문자 변환. 폭지정과 -플래그로 좌측 정렬이 가능하고 다른 옵션은 적용되지 않습니다.
- %s : 스트링 변환. 폭.최대문자수 형식으로 지정할 수 있습니다. 폭보다 문자열이 길어도 모두 표시하므로 주의해야 합니다. 또한 폭보다 작은 경우 기본은 우측 정렬이고 좌측 정렬을 하려면 -플래그를 사용해야 합니다. 다른 옵션은 적용되지 않습니다.
- %p : 포인터의 주소 표시. 포인터를 인수로 받는 스트링이나 포인터가 널인 경우 시스템에 따라 null을 표시하는 경우도 있고 비정상 종료되는 경우도 있으므로 주의해야 합니다.
printf("|%5c|%-5c|%5s|%-5s|%5.3s|%-5.3s|%p|\n", 'A', 'Z', "ABCDEF", "ABCDEF", "ABCDEF", "ABCDEF", "ABCDEF");
| A|Z |ABCDEF|ABCDEF| ABC|ABC |00DE5890|
☞ 폭, 최소길이, 정밀도 길이 제어
printf("|%*.*f|%-*.*f|%+*.*f|%*.*s|\n", 10, 3, 23.1234, 10, 3, 23.1234, 10, 3, 23.1234, 5, 3, "ABCDEFG"); printf("|%*.*f|%-*.*f|%+*.*f|%-*.*s|\n", -10, 3, -23.1234, -10, 3, -23.1234, -10, 3, -23.1234, 5, 3, "ABCDEFG");
| 23.123|23.123 | +23.123| ABC| |-23.123 |-23.123 |-23.123 |ABC |
각 타입변환 타입의 폭과 정수형의 최소길이, 스트링의 최대길이, 실수의 정밀도 등을 변수 값으로 가변적으로 제어 하고 싶은 경우에는 *문자를 숫자 대신 형식에 기술하고 값으로 전달하는 인수 앞에 정수값으로 폭이나 길이를 지정할 수 있습니다. 폭의 경우 음수값이면 -플래그 효과를 내면서 절대값을 폭으로 사용합니다.
'C | C++' 카테고리의 다른 글
형식화된 입력 (0) | 2018.06.04 |
---|---|
가변 인수 포맷 입출력 (0) | 2018.06.04 |
블럭 입출력 (0) | 2018.06.04 |
스트림 문자 및 라인 입출력 (0) | 2018.05.31 |
스트림 입출력 개관 (0) | 2018.05.31 |