[ft_printf](11)ft_print테스트


이번 포스트는 ft_print테스트에 관한 내용입니다.


1️⃣ 테스트진행(42TESTERS - PRINTF프로그램 이용)

  • ft_printf과제의 테스터 프로그램이 여러가지가 있지만 그 중에서 42TESTERS-PRINTF테스터 프로그램을 이용하여 테스트를 진행했습니다.

테스터 프로그램 링크 >> Mazoise - 42TESTERS-PRINTF

< 현재 나의 ft_printf 테스트 결과 >

42TESTERS_result_image.png

  • 틀린 케이스결과는 diff.txt파일에서 자세히 확인할 수 있습니다.

< diff.txt >

42TESTERS_diff_file_image.png


2️⃣ 오류 케이스1

(1) %0. + (d, i, u, x, X, o)출력값 오류

----------테스트 케이스----------
%0.d, %0.d, %0.d, %0.d, %0.d" // 1st '*' = -4, 2nd '*' = 0

/* 나의 출력 */
8, -12, 123456789, 0, -12345678, 97, -2147483648, 2147483647 --- Return : 60
/* 올바른 출력 */
8, -12, 123456789, , -12345678, 97, -2147483648, 2147483647 --- Return : 59
  • printf("%0.d", 0);의 케이스가 올바르지 않았습니다.
  • %0.d일 때 1, 2, ,3 ,4 …등은 그대로 출력되지만 0일 경우 공백을 출력해줘야 됩니다. result_ubuntu_image.png

  • Ubuntu Vscode(wsl)에서는 위처럼 %0.d처럼 서식자를 사용하는 것에 대해 오류를 출력해 주었기 때문에 따로 예외처리없이 코드를 구현했습니다.
  • 하지만 42Seoul의 과제는 MacOs운영체제를 기준으로 하는 과제이기 때문에 그 규칙을 따라야 합니다.

(2) 해결법

  • 정수값의 숫자부분 문자열을 만들어주는 함수인 set_print_cpy안에서 ft_ullitoa_cpy을 사용한 직후에 다음과 같은 예외처리 코드를 추가해줬습니다.
if (fg->point == 1 && fg->padding_back == 0 && fg->result[0] == '0' && fg->result[1] == '\0')
    fg->result[0] = '\0';




3️⃣ 오류 케이스2

(1) 숫자 정밀도가 음수일 때(%.-n) 출력값 오류

----------테스트 케이스----------
"%.*i, %.*d, %.*d, %.*d, %.*d, %.*d, %.*d, %.*d" // 1st '*' = -4, 2nd '*' = 0

/* 나의 출력 */
0008, -0012, 123456789, 0000, -12345678, 0097, -2147483648, 2147483647 --- Return : 70
/* 올바른 출력 */
8, -12, 123456789, 0, -12345678, 97, -2147483648, 2147483647 --- Return : 60
  • .*를 이용하여 음수를 받아올 때 양수부분의 값만큼 정밀도옵션을 적용해주어 0을 출력해준 것이 문제였습니다.
  • 역시 wsl환경에서는 적절한 사용의 방법이 아니라고 경고메시지를 출력해주었습니다.
  • 하지만 역시나 macOs환경에 맞추어 예외처리코드를 구현해 주어야 합니다. result_ubuntu_image.png

  • 위의 경우처럼 .*을 이용하는 것이 아닌 .-2d와 같이 직접적으로 음수값을 정밀도 옵션에 넣을 때는 macOs환경에서도 경고 메시지를 출력해 주었습니다.

(2) 해결법

  • 기존에 코드를 짤 때 정밀도옵션에 음수가 들어오지 않는다는 가정으로 구현했습니다.
  • 그래서 정밀도옵션을 읽어드리는 코드가 음수도 읽어드릴 수 있도록 바꿨습니다.
else if (fg->point == 1)  // 포인트(.)뒤를 읽을 때
{
	if (**format == '-')
		fg->back_minus = 1;  // back_minus은 새로운 구조체변수로 선언
	else
		fg->padding_back = 10 * fg->padding_back + (**format - '0');
}

/* 모든옵션을 읽어드린 후 */
if (fg->back_minus == 1)  // back_minus가 1이라면
	fg->padding_back *= -1;  // 정밀도 저장값을 음수로 만듬

(3) 또다른 오류

----------테스트 케이스----------
"%.*i, %.*d, %.*d, %.*d, %.*d, %.*d, %.*d, %.*d" // 1st '*' = -4, 2nd '*' = 0

/* 나의 출력 */
8, -12, 123456789, , -12345678, 97, -2147483648, 2147483647 --- Return : 59
/* 올바른 출력 */
8, -12, 123456789, 0, -12345678, 97, -2147483648, 2147483647 --- Return : 60
  • 37번테스트의 케이스 대부분이 통과하였습니다. 하지만 printf("%.*d", -4, 0);의 케이스에서 오류가 났습니다.
  • printf("%.*d", -4, 0);0이 출력되야 합니다.
  • 규칙이라고 하기에는 약간 애매(?)한 부분이 있지만 이와 같이 출력이 되도록 다시 구현해야 됩니다.
/* 삭제할 코드 */
fg->padding_back = (fg->padding_back < 0) ? 0 : fg->padding_back;  // 필요없는 코드
  • 위의 코드는 fg->padding_back이 음수일때 0으로 바꿔주는 코드입니다.
  • 하지만 fg->padding_back음수일때 0은 표시되고 0일때 ``(공백)이 출력되게 해야되기 때문에 굳이 위의 코드가 필요없어졌습니다.
  • ‘0문자열을 공백으로 바꾸는 조건문’‘케이스1’의 해결법으로 구현했던 코드에 fg->back_star == 0조건을 추가한 코드입니다.
/* 정밀도옵션이 0일때도 처리해주는 조건문 */
if (fg->point == 1 && fg->padding_back == 0 && fg->result[0] == '0' && fg->result[1] == '\0')
    fg->result[0] = '\0';
  • 위의 코드는 기존 %0.d를 처리하기 위해 만든 조건문이지만 알고리즘적으로 %.0d까지도 처리해줄 수 있는 조건문이 됬습니다.


4️⃣ 오류 케이스3

(1) 0플래그와 정밀도 옵션이 동시에 있을 때 출력값 오류

----------테스트 케이스----------
"%0*.*i, %0*.*d, %0*.*d, %0*.*d, %0*.*d, %0*.*d, %0*.*d, %0*.*d" // 1st '*' = 1, 2nd '*' = 0

/* 나의 출력 */
8, -12, 123456789, 0, -12345678, 97, -2147483648, 2147483647 --- Return : 60
/* 올바른 출력 */
8, -12, 123456789, , -12345678, 97, -2147483648, 2147483647 --- Return : 60
  • printf("%01.0d", 0);의 출력에서 올바른 출력을 하지 못했습니다. (0플래그와 정밀도옵션이 동시에 있는 경우)
  • 코드를 구현했을 때 0플래그와 .n(정밀도 옵션)을 동시에 주면 컴파일 경고를 주었기 때문에 따로 처리를 하지않았습니다.

(2) 해결법

  • 정수출력에서 0플래그를 주면 남는 공백만큼 0을 붙여서 출력해줍니다.
  • 하지만 정밀도 옵션이 포함된다면 0플래그를 무시해줘야하고 이러한 예외처리를 추가 해줬습니다.

< 기존 '0'플래그가 있을경우 처리코드 >

if (fg->zero == 1)
{
    /* 코드 생략 */
}
else if (fg->zero == 0)
{
    /* 코드 생략 */
}

< 수정된 '0'플래그가 있을경우 처리코드 >

if (fg->zero == 1 && fg->point == 0)  //정밀도옵션이 없을 때 조건 추가
{
    /* 코드 생략 */
}
else                    // else로 처리
{
    /* 코드 생략 */
}
  • 그런데 정밀도가 음수일 경우 정밀도 옵션은 무시되고 0플래그 기능이 살아났습니다.
  • fg->padding_back변수는 음수일 경우 0이 되도록 바꿔줬기에 fg->back_minus변수를 이용하여 정밀도 옵션이 음수임을 판별했습니다.

< 정밀도가 음수일 경우를 예외처리한 조건문 >

if (fg->zero == 1 && (fg->point == 0 || fg->back_minus < 0)) // fg->back_minus변수를 이용하여 판단
{
    /* 코드 생략 */
}
else                    // else로 처리
{
    /* 코드 생략 */
}




5️⃣ 오류 케이스4

(1) 문자열서식자(s)의 최대너비옵션(.n)이 음수일 때 출력값 오류

----------테스트 케이스----------
"%.*s, %.*s, %.*s, %.*s, %.*s, %.*s, %.*s, %.*s" // 1st '*' = -4, 2nd '*' = 14

/* 나의 출력 */
, , , , , , , --- Return : 14
/* 올바른 출력 */
abcdefghijklmnop, -a, -12, 0, %%, -2147483648, 0x12345678, -0 --- Return : 61
  • %s(문자열 서식자)일 때 .n(정밀도, 최대 출력길이)옵션이 음수일 경우 무시되도록 처리가 필요했습니다.

< 기존 문자열서식자의 정밀도 옵션처리 코드 >

if (fg->point == 1)    // [. ]정밀도 옵션이 있을시
    cnt = (cnt > fg->padding_back) ? fg->padding_back : cnt;

< 기존 문자열서식자의 정밀도 옵션처리 코드 >

if (fg->point == 1 && fg->padding_back >= 0)    // fg->padding_back이 음수가 아닐 때 조건추가
    cnt = (cnt > fg->padding_back) ? fg->padding_back : cnt;




6️⃣ 오류 케이스5

(1) `%p`(포인터 주소서식자) 출력값 오류

----------테스트 케이스----------
"%p, %x, %p, %x, %p, %x, 0%p</b>, %x" // 1st '*' = 5, 2nd '*' = 5

/* 나의 출력 */
0xc7e1ab0, c7e1ab0, 0xc5729c0, c5729c0, 0x1, 1, 00x0</b>, 0 --- Return : 54
/* 올바른 출력 */
0xc7e1ab0, c7e1ab0, 0xc5729c0, c5729c0, 0x1, 1, 0(nil)</b>, 0 --- Return : 56
  • printf("%p", NULL);의 경우에 올바른 출력을 하지않았습니다.
  • 0x0으로 출력되었지만 올바른 출력(nil)입니다.

(2) 해결법

< 수정 전 %p서식자 출력코드 >

void print_p(va_list ap, t_flag *fg, const char c, int *len)
{
    set_print_cpy(ap, fg, c);
    fg->padding_front -= 2;
    if (fg->left != 1) // '-'왼쪽정렬이 아닐때
        ft_print_word(' ', fg->padding_front, len);
    ft_print_str("0x", len);
    ft_print_str(fg->result, len); // 서식지정자 출력 (메인 출력)

    if(fg->left == 1) // 뒤쪽 공백출력 판단
        ft_print_word(' ', fg->padding_front, len);
}

< 수정 후 %p서식자 출력코드 >

void print_p(va_list ap, t_flag *fg, const char c, int *len)
{
    set_print_cpy(ap, fg, c);
	/* (nil)을 출력할 경우 fg->padding_front에 -4해줍니다. */
    fg->padding_front -= (fg->result[0] == '0' && fg->result[1] == '\0') ? 4 : 2;
    if (fg->left != 1) // '-'왼쪽정렬이 아닐때
        ft_print_word(' ', fg->padding_front, len);
    if (fg->result[0] == '0' && fg->result[1] == '\0')  // 주소값이 0일경우 조건문
        ft_print_str("(nil)", len);
    else
    {
        ft_print_str("0x", len);
        ft_print_str(fg->result, len); // 서식지정자 출력 (메인 출력)
    }
    if(fg->left == 1) // 뒤쪽 공백출력 판단
        ft_print_word(' ', fg->padding_front, len);
}




7️⃣ 오류 케이스6

(1) %s에 NULL이 들어올 때 출력값 오류

----------테스트 케이스----------
"%-2s, %.s, %-4s, %-2.4s, %-8.12s, %3s, %8s, %---2s, %.*s, %.0s, %.1s, %.2s, %.4s, %.8s" // 1st '*' = 12, 2nd '*' = 18
/* 나의 출력 */
Segmentation fault (core dumped)
/* 올바른 출력 */
(null), , (null), , (null) , (null), (null), (null), (null), , , , , (null) --- Return : 80
  • %s(문자열 서식자)의 가변인자NULL이 들어오면 cnt = ft_strlen(temp)코드에서 Segmentation fault (core dumped)가 되었습니다.
  • 그렇기 때문에 ft_strlen함수를 호출하기 전에 NULL에 대한 예외처리를 해주었습니다.

< 수정 후 %p서식자 출력코드 >

char* temp;

temp = va_arg(ap, char*);
cnt = ft_strlen(temp);

< 수정 후 코드 >

char* temp;

temp = va_arg(ap, char*);
if (temp == NULL)
    temp = "(null)";
cnt = ft_strlen(temp);

(3) 또다른 오류

----------테스트 케이스----------
"%-2s, %.s, %-4s, %-2.4s, %-8.12s, %3s, %8s, %---2s, %.*s, %.0s, %.1s, %.2s, %.4s, %.8s" // 1st '*' = 12, 2nd '*' = 18

/* 나의 출력 */
< (null), , (null), (nul, (null) , (null), (null), (null), (null), , (, (n, (nul, (null) --- Return : 89
/* 올바른 출력 */
> (null), , (null),   , (null)  , (null),   (null), (null), (null), , , , , (null) --- Return : 80
  • NULL값이 들어올 때 정밀도6((null)길이)보다 짧으면 출력되면 안됩니다.
  • 정밀도가 음수일 경우 정밀도옵션이 무시됩니다.

< 최종 구현된 %s(문자열)출력 코드 >

void print_s(va_list ap, t_flag *fg, int *len)
{
    int cnt;    // 실제 출력할 문자열길이
    char* temp;

    temp = va_arg(ap, char*);
    if (temp == NULL)
    {
        temp = "(null)";
        cnt = (fg->point == 1 && fg->padding_back < 6 && fg->padding_back > 0) ? 0 : 6;
    }
    else
        cnt = ft_strlen(temp);
    if (fg->point == 1 && fg->padding_back >= 0)    // [. ]정밀도 옵션이 있을시
        cnt = (cnt > fg->padding_back) ? fg->padding_back : cnt;
    fg->padding_front -= cnt;
    if (fg->left != 1) // 앞쪽너비
        fg->padding_front -= ft_print_word(' ', fg->padding_front, len);
    write(1, temp, cnt);
    *len += cnt;
    if (fg->left == 1) // 뒤쪽너비
        fg->padding_front -= ft_print_word(' ', fg->padding_front, len);
}




42TESTERS 통과!!

42TESTERS_done_image.png




© 2021.02. by kirim

Powered by kkrim