[ft_printf](5)정수형(d, i)플래그 옵션 처리
이번 포스트는 (ft_printf)서식 플래그, 서식 정밀도 처리에 관한 내용입니다.
1️⃣ 부호있는 10진수 서식지정자(d, i) 규칙
- 서식지정자의 종류에 따라서 적용되는 플래그규칙들이 달랐습니다.
- 모든 서식지정자를 고려해서 코드를 구현하는 것은 까다롭기 때문에 가장 기본적인 서식지정자인 부호있는 10진수 서식지정자(d, i)의 서식 플래그와 서식 정밀도를 적용하는 코드를 먼저 구현할 계획입니다.
- 먼저 C언어 내장함수인
printf함수
를 통해 플래그 규칙을 알아 보았습니다.
(1) 플래그(+, -, #, ' ',0), 너비옵션
printf("%d끝\n", 1234);
printf("%8d끝\n", 1234);
printf("%+8d끝\n", 1234);
printf("%+8d끝\n", -1234);
printf("% 8d끝\n", -1234);
printf("% -8d끝\n", -1234);
printf("%-8d끝\n", 1234);
printf("% -8d끝\n", 1234);
printf("%08d끝\n", 1234);
printf("% 08d끝\n", 1234);
printf("% +8d끝\n", 1234); //컴파일 오류
printf("% 8d끝\n", 1234); //컴파일 오류
printf("%-08d끝\n", 1234); //컴파일 오류
1234끝
1234끝
+1234끝
-1234끝 // 음수일때는 '+'플래그 무시
-1234끝 // 음수일때 ' '플래그 무시
-1234 끝
1234 끝
1234 끝 // '-'와 ' '플래그는 동시 사용가능
00001234끝
0001234끝 // ' '와 '0'플래그는 동시 사용가능
- 너비값은 문자길이(부호포함)를 뺀만큼을 ' '(공백) 출력하고 ' 0 '플래그가 있을 시 ' 0 '으로 출력됩니다.
- ' '플래그와 ' - '플래그, ' '플래그와 ' 0 '플래그, ' + '플래그와 ' 0 '플래그는 동시에 사용가능하지만 다른 조합은 컴파일 에러가 납니다.
- ' '플래그는 출력되는 ' + '기호와 ' - '기호에 의해 무시됩니다.
- ' - '플래그 사용시 너비값은 문자길이(부호포함)를 뺀만큼을 ' '(공백)으로 출력값 뒤쪽에 출력해줍니다.
- ' + '플래그는 음수값을 출력할시 무시됩니다.
(2) 너비옵션[.옵션]
printf("%8.6d끝\n", 1234);
printf("% 6.6d끝\n", 1234);
printf("% 7.6d끝\n", 1234);
printf("%-8.6d끝\n", 1234);
printf("%+8.6d끝\n", 1234);
printf("%8.3d끝\n", 1234);
printf("%3.3d끝\n", 1234);
printf("%08.6d끝\n", 1234); //컴파일 오류
001234끝
001234끝
001234끝 //' '플래그와 동시 사용가능
001234끝 //공백이 ' '플래그와 합쳐져서 계산됨
001234 끝 // '-'플래그와 동시 사용가능
+001234끝 // '+'플래그와 동시 사용가능
1234끝
1234끝 // 출력값의길이가 너비옵션보다 크면 옵션무시
- 서식지정자 d, i의 .(소수점 값)에서 출력길이들 뺀 길이만큼을 ' 0 '로 출력 해줍니다.
- ' '플래그와 동시에 사용가능하나 옵션에서 공백출력값이 1개라도 있는경우 무시됩니다.
- ' - '플래그와 ' + '플래그와 동시에 사용가능 하지만 ' 0 '플래그와 동시사용이 불가능합니다.
- 출력값의 길이가 너비옵션값보다 크면 옵션이 무시됩니다.
(3) * . * 플래그
printf("%*.*d끝\n", 8, 6, 1234);
printf("% *.*d끝\n", 8, 6, 1234);
printf("%+*.*d끝\n", 8, 6, 1234);
printf("%*.*d끝\n", +8, 6, 1234);
printf("%-*.*d끝\n", 8, 6, 1234);
printf("%*.*d끝\n", -8, 6, 1234);
printf("%*d끝\n", 8, 1234);
printf("%.*d끝\n", 8, 1234);
printf("%8.*d끝\n", 6, 1234);
printf("%*.6d끝\n", 8, 1234);
printf("%0*d끝\n", 8,1234);
printf("%0*.*d끝", 8, 6, 1234); //컴파일 오류
1234끝
001234끝
001234끝
+001234끝
001234끝 //매개변수에 적힌 +는 무시
001234 끝
001234 끝 //매개변수에 적힌 -는 적용
1234끝 //*플래그를 개별적으로 사용가능
00001234끝
001234끝 //*플래그를 직접입력한 숫자옵션과 조합가능
001234끝
00001234끝 //*플래그만을 사용하면 '0'플래그와 같이 사용가능
- ' * . * '플래그는 ap(매개변수포인터)로 부터 다음과 같이 값을 읽어옵니다.
int temp = va_arg(ap, int);
- ' - '플래그는 직접 플래그옆에 적을때 뿐만아니라 매개변수값에 붙여서 써도 적용이 되었습니다.
- ' + '플래그는 직접 플래그옆에 적을때만 적용되었습니다.
- ' * '플래그 혹은 ' . * '플래그는 개별적으로 사용이 가능하며 직접입력한 너비옵션과 조합이 가능합니다.
- ' 0 '플래그와 동시에 적용이 안되나 ' * '플래그만을 사용하면 동시에 사용이 가능했습니다.
2️⃣ set_form함수 수정
- 서식자지정함수를 호출하기전 앞으로 구현할 set_flag함수가 오도록 지정했습니다.
- 플래그 옵션에 사용될 구조체를 선언해주고 0으로 초기화 해주었습니다.
- 구조체를 포인터 형식으로 주고 받으며 사용할 계획입니다.
< set_form함수 >
void set_form(va_list ap, const char **format, int *len)
{
t_flag fg = { 0, };
set_flag(ap, &fg, format);
form_spec(ap, *format, &fg, len);
(*format)++;
}
3️⃣ t_flag구조체 구현[ d,i옵션만 가진 임시 구조체 ]
< t_flag구조체 >
typedef struct flags
{
int left;
int zero;
int minus;
char plus;
int hash;
int padding_back;
int padding_front;
} t_flag;
- left는 ' - '플래그를 관리합니다.
- zero는 ' 0 '플래그를 관리합니다.
- minus는 ' - '부호를 관리합니다.
- plus는 ' + '플래그와 ' '플래그를 관리합니다.
- hash는 ' # '플래그를 관리합니다.
- padding_front는 출력값의 앞쪽 너비를 관리합니다.
- padding_back는 출력값의 뒤쪽 너비를 관리합니다.
4️⃣ set_flag함수 구현[ d,i옵션만 고려한 임시 함수]
- set_flag함수는 출력하기 전 플래그와 너비옵션에 대해서 정리해주는 함수 입니다.
- 이 함수를 통해 플래그와 너비 옵션이 정리된 후 form_spec함수가 호출되어 최종적으로 문자열을 출력을 하게 됩니다.
< set_flag함수 >
void set_flag(va_list ap, t_flag *fg, const char **format)
{
int back = 0;
int star;
/* 플래그 판단 while문 */
while (ft_strchr(FLAGS, **format) == TRUE)
{
if (**format == '-')
fg->left = 1;
if (**format == '+')//'+'와 ' '플래그는 동시에 적용안되기 때문에 하나의 변수로사용
fg->plus = '+';
if (**format == ' ')
fg->plus = ' ';
if (**format == '#') //#플래그는 x,X,o에서 사용될 예정
fg->hash = 1;
if (**format == '0')
fg->zero = 1;
(*format)++;
}
/* 너비옵션 판단 while문 */
while (ft_strchr(POSITION, **format) == TRUE)
{
if (**format == '.')
back = 1;
else if (**format == '*')
{
star = va_arg(ap, int); //int자료형으로 매개변수를 불러옴
if (star < 0)
{
fg->left = 1; // *옵션은 음수를 받을때 '-'플래그로 인식
star *= -1;
}
if (back == 0)
fg->padding_front = star;
else
fg->padding_back = star;
}
else if (back == 0) //back변수로 패딩위치를 판단
{
fg->padding_front = 10 * fg->padding_front + (**format - '0');
} //**format은 char형이기 때문에 -'0'을 하여 정수형으로 복사함
else if (back == 1)
{
fg->padding_back = 10 * fg->padding_back + (**format - '0');
}
(*format)++;
}
}
5️⃣ form_spec함수 세분화(d,i)[ 서식지정자함수 ]
< 세분화전 d,i처리문 >
if (*c == 'd' || *c == 'i' || *c == 'u' || *c == 'x' || *c == 'X' || *c == 'o')
{
/* 코드 생략 */
else if (*c == 'i' || *c == 'd')
{
if (temp < 0)
{
write(1, "-", 1);
*len++;
temp *= -1;
}
if(!(result = ft_ullitoa_malloc(temp,DIGITS)))
return ;
}
/* 코드 생략 */
}
< 세분화후 d,i처리문 >
if (*c == 'd' || *c == 'i')
{
*len += print_di(ap, fg);
}
- 코드의 길이가 너무 길어져서
print_di
함수를 새로 구현하여 세분화 시켰습니다.
< print_di함수 >
int print_di(va_list ap, t_flag *fg)
{
char *result;
int cnt;
int cnt2;
long long int temp;
temp = va_arg(ap, int);
if (temp < 0) // 출력할 정수 분호 판별
{
temp *= -1;
fg->minus = 1;
cnt++;
}
cnt = 0;
result = ft_ullitoa_malloc(temp, DIGITS); // 정수->문자 변환
cnt2 = ft_strlen(result); // 출력할 문자길이
/* 앞,뒤 너비옵션이 출력문자길이보다 짧으면 의미가 없으므로 0으로 지정 */
fg->padding_front = (fg->padding_front > cnt2) ? (fg->padding_front - cnt2) : 0;
fg->padding_back = (fg->padding_back > cnt2) ? (fg->padding_back - cnt2) : 0;
if (fg->plus != 0 && fg->minus != 1)// 맨앞 ' ' or '+' 출력 판단
{
write(1, &(fg->plus), 1);
fg->padding_front--;
cnt++;
}
if (fg->left != 1) // '-'왼쪽정렬이 아닐때
{
while (fg->zero == 1 && fg->padding_front > 0)
{
write(1, "0", 1);
fg->padding_front--;
cnt++;
}
while (fg->zero == 0 && fg->padding_front - fg->padding_back > 0)
{
write(1, " ", 1);
fg->padding_front--;
cnt++;
}
while (fg->zero == 0 && fg->padding_back > 0)
{
write(1, "0", 1);
fg->padding_back--;
cnt++;
}
}
else // '-'왼쪽정렬일 때
{
fg->padding_front -= fg->padding_back;
while (fg->padding_back > 0)
{
write(1, "0", 1);
(fg->padding_back)--;
cnt++;
}
}
if (fg->minus == 1) // 출력할 정수가 음수일 때
write(1, "-", 1);
cnt += ft_print_str(result); // 서식지정자 출력 (메인 출력)
while(fg->padding_front > 0 && fg->minus == 1) // 뒤쪽 공백출력 판단
{
write(1, " ", 1);
fg->padding_front--;
cnt++;
}
free(result); // ft_ullitoa_malloc함수 반환값 메모리 해제
return (cnt); // 출력한 숫자를 반환
}
- 단순히 규칙만을 생각해서 코드를 구현했기 때문에 코드의 길이도 길고 가독성이 떨어집니다.
- 코드 정리작업은 모든 코드를 구현한 뒤 정리할 계획입니다.