ft_split 구현

yeonjkim·2021년 5월 24일
0

42seoul-libft

목록 보기
23/43

이는 norm v3 이전에 구현한 코드입니다.

1. ft_split 용도

+ 파라미터로 들어오는 문자열 s를 `문자 c`를 기준으로 쪼개어 만든 `문자열들의 배열`을 만들어 반환하는 함수

+ 즉 이중 포인터에 저장하는 함수

2. ft_split 프로토타입

char	**ft_split(char const *s, char c)
char const *s : 쪼갤 문자열.
char c : 문자열을 나누는 기준이 되는 문자 c.

3. 구현 시 유의사항

<사람마다 코드를 구현하는 방식이 다르겠지만, 내가 코드를 짰을 때의 주의할 점을 서술하겠다>

1 ) 문자 c를 기준으로 쪼개어 만들어진 문자열의 개수를 셀 때 주의점.

  • 문자 c와 문자 c가 아닌 것의 쌍으로 문자열의 개수를 세었다면 s의 제일 첫번째 값이 c인지 c가 아닌지 유의해야 한다.

    예를 들어 s가 "!abc!bca!cab"이고, c가 '!'라면 c와 c가 아닌 것의 쌍은 "!a", "!b", "!c"로 총 3개라 나뉘어진 문자열 "abc", "bca", "cab"인 3개와도 일치한다.

    그러나 s가 "abc!bca!cab"이고 c가 '!'라면 c와 c가 아닌 것의 쌍은 "!b", "!c"로 총 2개라 나뉘어진 문자열 "abc", "bca", "cab"인 3개와 일치하지 않는다.

    따라서 먼저 's의 첫 글자가 c가 아니'라면 '문자열의 개수를 하나 증가'시키고, 그 다음 두번째 글자부터 'c와 c가 아닌 것의 쌍'을 체크해 문자열의 개수를 세는 것이 좋다.

2 ) 이차원 배열을 할당하고, 다시 일차원 배열을 할당해 그 안에 원소를 넣을 때 주의점.

  • 나의 경우에는 제일 첫번째 원소가 c가 아닐 때, 그리고 두번째 원소부터 c와 c가 아닌 것의 쌍이 나올 때 ft_makestr함수를 호출했는데, 이 ft_makestr함수에서는 호출한 s의 인덱스 이후부터 c가 나오기 전까지를 ft_strndup()을 이용해 일차원 배열에 문자들을 넣어 문자열을 만든다.

    그러나 이 때 유의해야 할 점이, 바로 c가 나오기 바로 전 인덱스까지 붙여 넣는 것이다. 만약 c가 나오지 않고 s가 끝나버렸다면 일차원 배열에는 s의 끝까지 붙여넣어야 한다. 이 점을 주의하자.

3 ) 할당 실패 시 지금까지 만들었던 배열들 모두 free하기.

  • 할당을 실패해서 NULL을 반환한다 하더라도 반환 전에 free해 주지 않으면 메모리 누수가 생긴다. 따라서 이차원 배열 str에 들어 있는 값들을 모두 free해 주는 함수 ft_malloc_error를 구현한다.

4. 코드 구현

#include "libft.h"

static char             **ft_malloc_error(char **str)
{
        int     index;

        index = 0;
        while (!str[index])
        {
                free(str[index++]);
        }
        free(str);
        return (NULL);
}

static char             *ft_strndup(const char *s, int len)
{
        char    *res;
        int             index;

        res = NULL;
        index = 0;
        if (len == 0)
                return (NULL);
        if (!(res = (char*)malloc(sizeof(char) * (len + 1))))
        {
                return (NULL);
        }
        while (index < len)
        {
                res[index] = s[index];
                index++;
        }
        res[index] = '\0';
        return (res);
}

static char             *ft_makestr(const char *s1, char c, int *flag)
{
        char    *middle;
        int             index;

        *flag = 1;
        middle = NULL;
        index = 0;
        while (s1[index] != '\0')
        {
                if (s1[index] == c)
                {
                        middle = ft_strndup(s1, index);
                        if (!middle)
                                return (NULL);
                        return (middle);
                }
                else if (index == (int)(ft_strlen(s1)) - 1)
                {
                        middle = ft_strndup(s1, index + 1);
                        if (!middle)
                                return (NULL);
                        return (middle);
                }
                index++;
        }
        return (NULL);
}

static int              ft_word_count(const char *s, char c)
{
        int             wc;
        int             index;

        wc = 0;
        index = 0;
        while (s[index] != '\0')
        {
                if ((index == 0 && s[0] != c) ||
                (s[index] == c && s[index + 1] != c && s[index + 1] != '\0'))
                {
                        wc++;
                }
                index++;
        }
        return (wc);
}

char                    **ft_split(const char *s, char c)
{
        char    **str;
        char    *middle;
        int             flag;
        int             index;
        int             strindex;

        strindex = 0;
        index = 0;
        if (!(str = (char**)malloc(sizeof(char*) * (ft_word_count(s, c) + 1))))
                return (NULL);
        while (s[index] != '\0')
        {
                flag = 0;
                if (index == 0 && s[0] != c)
                        middle = ft_makestr(&s[index], c, &flag);
                else if ((s[index] == c && s[index + 1] != c && s[index + 1] != '\0'))
                        middle = ft_makestr(&s[index + 1], c, &flag);
                if (flag == 1 && middle == NULL)
                        return (ft_malloc_error(str));
                else if (flag == 1)
                        str[strindex++] = middle;
                index++;
        }
        str[strindex] = 0;
        return (str);
}

5. 코드 구현 방법(함수 별)

1 ) ft_split() : ft_word_count를 이용해 쪼개지는 모든 문자열의 개수를 세서 이차원 배열 str에 할당한다. norminette규정을 맞추어야 하기 때문에 flag를 이용해 구현했다.

flag는 동적 할당을 실패했는지 성공했는지 유무에 따라 함수가 어떻게 동작할지 결정하는 변수이다.

만약 make_str()을 통해 middle을 할당했는데 이 middle이 NULL이라면 지금까지 할당한 메모리를 free하는 ft_malloc_error함수를 호출한다.
만약 middle이 NULL이 아니라면 str에 쪼갠 문자열을 넣는다.
str에 쪼갠 문자열을 모두 넣었으면 맨 마지막엔 0(NULL)을 넣고 리턴한다.

2 ) ft_word_count() : 문자열 s를 문자 c를 기준으로 나눈 문자열의 총 개수를 리턴한다.
만약 s의 제일 첫번째 문자가 c가 아니거나 s[index]가 c이고 s[index + 1]이 c라면 문자열의 개수를 증가시켜 반환한다.

3 ) ft_makestr() : s[index]가 c가 아닐 때부터 ft_makestr함수에 s[index]의 주소가 들어온다. 따라서 s[index]가 c이기 전까지 혹은 s[ft_strlen(s) - 1]까지 문자열을 할당해 리턴하면 된다. 문자열을 할당해 리턴할 때 ft_strndup()을 이용하는데, 이 때 문자열의 길이가 필요하기 때문에 s[index] == c와 index == ft_strlen(s) - 1 일 때를 나누어서 호출하면 된다.

s1[index] == c일 때에는 c의 전까지 문자열을 만들면 되기 때문에 ft_strndup으로 전달되는 len은 index만큼이다.

다만 index == ft_strlen(s) - 1의 경우에는 s의 끝까지 문자열을 만들어야 하므로 ft_strndup으로 전달되는 len은 index + 1만큼이다.

즉 가장 큰 차이는 index 바로 전까지 문자열을 만드느냐, index까지 문자열을 만드냐의 차이이다.

4 ) ft_strndup() : 문자열 s와 길이를 인자로 받아 len + 1만큼 메모리를 할당하고 len만큼 요소를 옯겨 문자열을 만드는 함수.

5) ft_malloc_error() : 지금까지 만들었던 이차원 배열 str을 할당 해제하고 NULL을 반환하는 함수.

일차원 배열을 모두 할당 해제한 후 이차원 배열까지 할당 해제.

6. 구현 시 어려웠던 점 혹은 테스트 케이스 fail 원인

(1) s의 제일 첫번째 원소가 c인지 아닌지 고려하지 않은 점

(2) 할당 실패 시 지금까지 할당했던 메모리를 free시키지 않은 점(테스트 케이스 fail은 아니나 메모리 누수가 발생해 디펜스를 못함)

(3) if (s1[index] == c || index == len - 1)로 ft_makestr()을 구현한 점.

이 경우에는 s의 맨 마지막 문자가 c일 때, c를 포함해 문자열을 만들게 된다. 이는 ft_strndup의 len 파라미터의 값을 s1[index] == c일 때와 index가 len - 1일 때를 다르게 하지 못했던 점이다. 지금은 이를 if else if문으로 바꾸었기 때문에 제대로 동작한다.

0개의 댓글