개요

본 프로젝트는 stdio.hprintf()를 구현할 것을 요구한다.

지시 사항

구현 요구사항

구현할 함수의 프로토타입은 다음과 같다.

int ft_printf(const char *format, ...);

이때 libcprintf처럼 버퍼 관리를 수행할 필요는 없다. c s p d i u x X %의 총 9가지 변환을 수행해야 한다.

각 변환에 대해 다음 플래그를 구현한다:

각 사항별 상세

FreeBSD의 매뉴얼(man 3 printf)에서는 다음과 같이 명세한다.

형식 문자열

const char *format으로 주어지는 문자열에서 %가 아닌 문자는 그대로 출력 스트림에 복사한다. 이 과정 중 %를 만날 경우 형식 문자열을 파싱하려 시도하고, 파싱이 성공적일 시 각 변환이 요구하는 매개 변수를 소모하게 된다.

형식 문자열은 다음과 같이 구성된다.

{flags: #0- +}{minimum field width: 숫자}{precision: .과 숫자들}{conversion specifier: [cspdiuxX%]}

플래그

flags

최소 너비

minimum field width

변환된 결과의 길이가 최소 너비보다 짧을 시 공백을 그 수만큼 출력한다. 기본적으로 좌측에 출력하여 우측 정렬을 실시하고 '-' 플래그가 켜졌을 시 우측에 출력한다.

정밀도

precision

. 이후에 숫자를 적어서 지정한다. 숫자가 없을 시 0으로 지정된다.

변환 지정자

conversion specifier

반환값

출력한 문자의 총 개수를 반환한다. 오류가 발생했을 시 음의 정수를 반환한다.

구현 방안

발상

int ft_printf(const char *format, ...)

format부터 \\0을 발견할 때까지 문자를 순차적으로 탐색한다. %가 아닌 문자는 STDOUT에 그대로 출력한다. %를 마주칠 시 형식 문자열 해석기(이하 해석기)를 시작한다. 규격에 맞지 않는 문자 때문에 해석이 실패한 경우 도중의 결과를 폐기하고 문제가 되는 문자 다음부터 탐색을 재개한다. 해석이 성공했다면 가변 인자에서 그 형식이 요구하는대로 데이터를 입력받아 문자열로 변환한 후 STDOUT에 출력한다.

이때 2가지 선택지가 존재한다.

전자는 format의 문자를 순회하는 반복문 안에서 해석->변환->출력 과정이 모두 일어나야 하기 때문에 제어 구조가 길고 에러 핸들링이 복잡하다. 반면에 후자의 방식은 첫 반복문에서는 해석만 하고, 두번째 반복문에서는 변환 및 출력만 담당하기 때문에 첫 반복문에서는 가변 인자에 접근할 필요가 없고 파일 I/O 관련 에러가 발생하지 않는다. 본 프로젝트에서는 후자의 구조를 채택한다.

세부 모듈 고안

ft_printf의 구현에는 크게 2개 기능이 필요하다.

해석기

format을 여러 부분으로 나누어 어느 부분이 형식 문자열이고 어느 부분이 그대로 출력되어야 하는지, 그 부분의 개수는 몇 개이며 각 형식 지정자별로 어떤 플래그를 고려하여 변환을 수행해야 하는지 파악이 필요하다. 다음과 같은 문자열을 보자.

"Hello, %-7.5s!"
  1. Hello, 문자열
  2. s 변환 지정자, 왼쪽 정렬, 최소 너비 7, 정밀도 5
  3. ! 문자열

format을 3개의 부분으로 나누어 처리해야 한다. 이때 각 부분은 다음과 같은 정보를 가진다.

변환의 종류

  1. 변환 없음(이하 plain), c, s, p, d, i, u, x, X, %
  2. 원본 format에서 차지하는 부분
  3. '#' “alt form” 플래그 활성화 여부
  4. '0' “zeropad” 플래그 활성화 여부
  5. '-' “left” 플래그 활성화 여부
  6. ' ' “blank” 플래그 활성화 여부
  7. '+' “sign” 플래그 활성화 여부
  8. 최소 너비 “minimum field width” 활성화 여부
  9. 정밀도 “precision” 활성화 여부
  10. 최소 너비의 값
  11. 정밀도의 값

이러한 정보를 기록한 변수를 티켓이라고 명명한다. 해석기는 format 문자열을 해석하여 필요한 티켓을 필요한 개수만큼 발부한다.

위 문자열은 대략 다음과 같이 해석될 것이다.

  1. plain 변환, H부터 % 전까지, 모든 플래그 비활성화
  2. s 변환, left 플래그 활성화, 최소 너비 활성화, 정밀도 활성화, 최소 너비 7, 정밀도 5