[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'플래그는 동시 사용가능
  1. 너비값문자길이(부호포함)를 뺀만큼을 '  '(공백) 출력하고 ' 0 '플래그가 있을 시 ' 0 '으로 출력됩니다.
  2. '  '플래그' - '플래그, '  '플래그' 0 '플래그, ' + '플래그' 0 '플래그는 동시에 사용가능하지만 다른 조합은 컴파일 에러가 납니다.
  3. '  '플래그는 출력되는 ' + '기호' - '기호에 의해 무시됩니다.
  4. ' - '플래그 사용시 너비값문자길이(부호포함)를 뺀만큼을 '  '(공백)으로 출력값 뒤쪽에 출력해줍니다.
  5. ' + '플래그음수값을 출력할시 무시됩니다.

(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); // 출력한 숫자를 반환
}
  • 단순히 규칙만을 생각해서 코드를 구현했기 때문에 코드의 길이도 길고 가독성이 떨어집니다.
  • 코드 정리작업은 모든 코드를 구현한 뒤 정리할 계획입니다.




© 2021.02. by kirim

Powered by kkrim