본문 바로가기

프로그래밍/C

[library] split 구현하기

📌 매뉴얼 (in subject) 

더보기

Function name

     do_split

 

Prototype

     char **do_split(char const *s, char c);

 

Parameters

     s: The string to be split. / c: The delimiter character.

: s 는 나눠줄 문자열, c는 구분자 문자

 

Return value

     The array of new strings resulting from the split. NULL if the allocation fails.

: 나눠진 결과 문자열의 배열을 반환, 할당에 실패하면 NULL 반환

 

External functs

     malloc, free

 

Description

     Allocates (with malloc(3)) and returns an array of strings obtained by splitting ’s’ using the character ’c’ as a

     delimiter. The array must end with a NULL pointer.

: 할당하고 문자 'c'를 구분 기호로 사용하여 's' 를 분할해 얻은 문자열 배열을 반환한다.

배열은 NULL 포인터로 끝나야 한다.

📌 작성 코드

#include <stdlib.h>
#include <unistd.h>

static char	**do_free(char **s)
{
	int	i;

	i = 0;
	while (s[i])
	{
		free(s[i]);
		i ++;
	}
	free(s);
	return (NULL);
}

static int	do_wordcount(const char *s, char c)
{
	int	i;
	int	result;

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

static char	*do_strtok(char *s, char c, int *len, int *del_len)
{
	char	*tok;

	*len = 0;
	*del_len = 0;
	tok = s;
	while (*tok == c)
	{
		(*del_len)++;
		tok ++;
	}
	while (*tok != c && *tok != '\0')
	{
		tok ++;
		(*len)++;
	}
	*tok = '\0';
	return (s);
}

static char	**do_strtok_front(char *s, char c, int wordc)
{
	int		len;
	int		del;
	char	*str;
	char	**result;
	int		i;

	i = 1;
	result = (char **)do_calloc(sizeof(char *), (wordc + 1));
	if (result == NULL)
		return (NULL);
	str = do_strtok(s, c, &len, &del);
	result[0] = (char *)do_calloc(sizeof(char), (len + 1));
	if (result[0] == NULL)
		return (do_free(result));
	do_strlcpy(result[0], str + del, len + 1);
	while (i < wordc)
	{
		str = do_strtok(str + len + del + 1, c, &len, &del);
		result[i] = (char *)do_calloc(sizeof(char), (len + 1));
		if (result[i] == NULL)
			return (do_free(result));
		do_strlcpy(result[i++], str + del, len + 1);
	}
	return (result);
}

char	**do_split(char const *s, char c)
{
	char	**result;
	char	*str;
	int		wordc;

	if (s == NULL)
		return (NULL);
	str = do_strdup(s);
	if (str == NULL)
		return (NULL);
	wordc = do_wordcount(str, c);
	if (wordc == 0)
		result = (char **)do_calloc(sizeof(char *), 1);
	else
		result = do_strtok_front(str, c, wordc);
	if (result == NULL)
		return (NULL);
	free(str);
	return (result);
}

 

📌 코드 리뷰

파이썬, 자바 등에 존재하는 split 함수를 c에서 만들어보았다.

기본적인 매커니즘은 string.h에 존재하는 strtok(split과 비슷한 동작을 하는 함수)의 개념을 토대로 만들었다.

문자열(s)을 받아 특정문자(c)로 단어들을 나누고, 이차원 배열에 각각의 단어를 저장하는 함수이다.

 

우선, do_split 에서부터 시작한다.

const로 받아온 문자열을 직접 수정할 것이므로, 이전에 만든 do_strdup를 이용해 복사해 넣어줄 것이다.

그렇게 문자열 str이 만들어졌고, 이 str이 할당되지 않았다면 NULL을 반환하여 예외처리해준다.

 

그리고 새로 구현한 do_wordcount 함수를 가져와 wordc에 넣어주는데 이는 가져온 문자열을 구분자를 통해

구분하여, word의 수를 파악할 수 있게 해주는 함수이다. 반환은 'word 개수'이다.

만약, 이 wordc의 값이 0이라면, 단어가 존재하지 않는 것인데, 이 상황에서는 result 에 do_calloc으로 1만큼의 공간만

할당하여, 빈문자열을 넣어준다. 그 외에 wordc가 존재한다면, else를 통해서 do_strtok_front 함수 동작으로 넘어간다.

 

do_strtok_front 함수에서는 do_strtok를 불러올 메인 동작을 담당한다.

우선 단어들을 담기 위해, 더블 포인터 char 형으로 do_calloc을 통해 result에 할당한다. 할당에 실패하면 NULL을 반환.

그리고, do_strtok 동작으로 넘어간다.

 

do_strtok 함수에서는 기존 문자열의 첫 부분 주소를 가져와 tok 에 먼저 할당한다.

할당한 *tok가 c에 해당하는 문자에 해당되면 tok의 주소값을 증가시켜주고,

del_len으로 설정한 '구분자가 나오는 길이 값'도 증가시켜준다.

-> 이는 구분자가 여러번 이어 작성되었을 때, 단어 저장시 구분자가 포함되어 저장되지 않도록 해주기 위함이다.

(do_strtok_front 함수에서 result에 저장할 때 + del 이 되면서 구분자 부분을 빼준다.)

 

그리고 단어가 나오는 부분 (*tok이 c가 아닌 부분)과 해당 위치가 null이 아닌 값이 존재하는 동안 tok 주소값을 증가하며

단어 길이인 len도 증가시켜준다. (이는 추후에 동적할당을 위해 len을 가져오기 위함.)

(-> *len ++ 로 하면 주소가 len의 자료형만큼 증가 되는거고, (*len) ++ 를 해야 해당 len의 값이 증가된다.)

tok 로 가리키던 마지막 부분에 '\0'를 넣어주면서 단어를 끊어주고, 기존 문자열 첫 주소를 가리키던 s를 반환한다.

 

그리고 다시 do_strtok_front로 돌아가 result[0] 부분을 방금 구한 len + 1 만큼 do_calloc으로 할당해주고,

예외처리 후 do_strlcpy로 단어를 copy해서 넣어준다. 여기서 인자를 보면,

result[0]에 복사하여 넣을 것이고, str 에서 del 만큼만 더해줘서 첫 단어의 첫 위치부분을 가리키도록 하고,

그 위치에서부터 len + 1까지만 읽어서 copy 해준다.

 

그럼 result[0]에는 첫 단어가 저장이 될 것이고, 이러한 과정들을 while을 통해서 wordc 까지 반복하면 끝이다.

while 문에서는 do_strtok에 str + len + del + 1 이라는 인자를 넣게 되는데, 방금 구해진 len과 del 그리고 +1 을 해서

첫번째 문자가 끝난 다음부분 부터 읽어가도록 한다. 그리고 같은 과정을 수행하고, while문이 돌 때마다

do_strtok 인자에 들어갈 str이  str + len + del + 1 로 바뀌어 들어가므로, 다음 단어를 계속 볼 수 있게 된다.

모든 단어를 읽고 나면 result를 반환한다.

 

do_split으로 돌아와서 예외처리를 해주고, free(str)로 str은 메모리를 비워준다.

(이전에 str을 free 하지 않아서 메모리 누수가 있었는데, 이를 추가하여 alloc 과 free가 정상작동하여 leak가 없게 했다.)

그리고 result를 반환하면서 모든 단어들이 result에 각각 저장되어 split이 수행되어지면서 끝난다.

 

 

 

 

 

 

 

코드가 심히 길어지고 불필요한 부분이 많아진 점이 아쉽기 때문에 추후에 재작성해볼 split함수 이다.

strtok 개념을 꼭 탑재하고 싶어서, 더 쉽게 갈 수 있는데 돌아간 부분이 없지 않아 있다.

split의 기본동작만 생각하고 추후에 다시 작성해보도록 하겠다.

'프로그래밍 > C' 카테고리의 다른 글

[graphic] miniLibX Manual  (0) 2024.08.06
[library] itoa 구현하기  (0) 2024.06.29
[library] strtrim 구현하기  (0) 2024.06.27
[library] strjoin 구현하기  (0) 2024.06.26
[library] substr 구현하기  (0) 2024.06.18
Recent Posts
Popular Posts
Recent Comments