get_next_line 구성

get_next_line 과제에서는 총 세가지 파일을 제출해야한다. get_next_line.c, get_next_line_utils.c, get_next_line.h.
libft를 사용할 수 없는 대신 _utils.c가 허용되어 총 10개의 함수를 사용할 수 있다.
배열을 이용하여 기능을 구현하였으며, 사용한 함수들은 다음과 같다.

char	*get_next_line(int fd)
static char	*check_remain(int fd, char *remain)
static char	*make_next_line(char *remain)
static char	*update_remain(char *remain)
size_t	ft_strlen(const char *str);
char	*ft_strchr(const char *str, int c);
size_t	ft_strlcpy(char *dst, char *src, size_t size);
char	*ft_strdup(const char *src);
char	*ft_strjoin(char const *str1, char const *str2);

코드 분석

get_next_line 함수

char	*get_next_line(int fd)
{
	static char	*remain = NULL;
	char		*next_line;

	if (fd < 0 || BUFFER_SIZE <= 0)
		return (NULL);
	remain = check_remain(fd, remain);
	if (!remain)
		return (NULL);
	next_line = make_next_line(remain);
	remain = update_remain(remain);
	return (next_line);
}

메인에서 직접 호출될 함수로, 이곳에서 정적 변수를 선언하여 써먹게된다.

  • static char *remain : 파일 디스크립터를 read함수로 읽은 후 남은 문자열이라는 의미로 remain이라는 이름을 붙였다.
  • char *next_line : 함수의 반환 목표로써, 출력할 한 줄의 문자열이다.

get_next_line함수의 작동과정은 다음과 같다.

  1. 인자로 받아온 fd값과, 컴파일시 -D 옵션으로 지정한 변수 BUFFER_SIZE의 값을 체크한다. fd < 0이거나 BUFFER_SIZE <= 0 이라면 정상적인 입력이 아니므로 NULL을 리턴한다.
  2. check_remain 함수를 통해 remain에 문자열이 남아있는지 체크한다.
  3. remain이 없다면 NULL을 리턴한다.
  4. 있다면 make_next_line 함수를 통해 remain에 남아있는 문자열에서 \n까지의 한 줄을 읽어 next_line변수에 저장한다.
  5. update_remain 함수를 통해 next_line으로 저장할 한 줄을 빼고 나머지를 remain으로 저장한다.
  6. next_line을 리턴한다.

check_remain 함수

static char	*check_remain(int fd, char *remain)
{
	char	*buff;
	char	*temp;
	int		read_idx;

	buff = (char *)malloc((BUFFER_SIZE + 1) * sizeof(char));
	while (!remain || !ft_strchr(remain, '\n'))
	{
		read_idx = read(fd, buff, BUFFER_SIZE);
		if (read_idx <= 0)
			break ;
		buff[read_idx] = '\0';
		if (!remain)
			remain = ft_strdup(buff);
		else
		{	
			temp = ft_strjoin(remain, buff);
			free(remain);
			remain = temp;
		}
	}
	free(buff);
	return (remain);
}

fd에서 데이터를 읽어들여 정적 변수 remain에 저장하는 함수다.
check_remain 함수의 작동과정은 다음과 같다.

  1. 변수 buffBUFFER_SIZE + 1(\0문자가 들어갈 공간)만큼 할당한다.
  2. remain이 없거나, remain에 남아있는 문자열에 \n이 없을 경우 반복문으로 들어간다.
  3. read함수로 fd에서 BUFFER_SIZE바이트만큼 데이터를 읽어와 buff에 저장한다.
    이 때 read함수의 반환값은 정상적으로 읽어들인 바이트의 크기가 된다.
    이를 read_idx 변수에 저장해 0보다 작으면 fd에 해당하는 파일을 끝까지 읽은것이므로 반복문을 빠져나와 파일 읽기를 멈춘다.
  4. 그렇지 않으면 읽어낸 데이터를 다루기 위해 buff[read_idx], 즉 buff의 마지막에 \0을 넣어준다.
  5. 만약 remain이 빈 상태였다면 ft_strdup함수를 이용해 remain에 새롭게 메모리를 할당하며 buff를 복사해준다.
  6. remain에 남아있는 데이터가 있었다면 ft_strjoin함수를 이용해 remain뒤에 buff를 붙인 새로운 문자열을 할당한다.
    • 이 때 ft_strjoin 함수로 반환되는 문자열은 기존의 remain과 다른 메모리를 가지고 있으므로, 메모리 누수를 피하기 위해서 기존 remain의 메모리를 해제한 후 새롭게 만든 메모리로 연결시켜주어야 한다.
  7. remain에서 \n을 찾을 수 있을 때까지 위 과정을 반복한다. 물론 중간의 탈출조건으로 반복문을 빠져나올 수도 있다.
  8. 역할이 끝난 buff의 메모리를 해제한 후, remain을 반환한다.

이 결과 만들어지는 remain은 셋 중 하나이다.

  • \n이 반드시 하나 이상 포함되어있는 문자열
  • \n이 없고, EOF에 도달한 문자열
  • NULL(처음부터 remain이 비어있었고, read함수로 읽어올 데이터도 남아있지 않았을 경우)

make_next_line 함수

static char	*make_next_line(char *remain)
{
	char	*next_line;
	int		len;

	len = 0;
	while (*(remain + len))
	{
		if (*(remain + len++) == '\n')
			break ;
	}
	next_line = (char *)malloc((len + 1) * sizeof(char));
	ft_strlcpy(next_line, remain, len + 1);
	return (next_line);
}

remain에서 \n을 포함한 한 줄의 문자열을 뽑아내는 함수다.
make_next_line 함수의 작동과정은 다음과 같다.

  1. remain을 읽으면서 next_line에 할당할 길이를 잰다. \n가 있다면 거기서 멈추고, 없다면 remain의 끝까지 잰다. \n가 없는 경우는 파일을 끝까지 읽은 것이다.
  2. 잰 길이로 next_line에 메모리를 할당한다.
  3. ft_strlcpy 함수를 이용해 next_lineremain의 문자열을 길이만큼 복사하고 마지막에 \0을 넣는다.
  4. 만들어진 next_line을 반환한다.

update_remain 함수

static char	*update_remain(char *remain)
{
	char	*temp;
	char	*fix_remain;

	fix_remain = ft_strchr(remain, '\n');
	if (!fix_remain)
	{
		free(remain);
		return (NULL);
	}
	temp = (char *)malloc(ft_strlen(fix_remain) * sizeof(char));
	if (!ft_strlcpy(temp, fix_remain + 1, ft_strlen(fix_remain)))
	{
		free(temp);
		free(remain);
		return (NULL);
	}
	free(remain);
	return (temp);
}

remian에서 next_line으로 출력할 문자열을 빼는 함수다.
update_remain 함수의 작동과정은 다음과 같다.

  1. ft_strchr 함수를 이용해 remain에서 \n의 위치를 찾아 fix_remain 변수에 저장한다.
  2. 찾을 수 없다면(fix_remain == NULL) 파일을 끝까지 읽은 경우이다. 더이상 remain을 남길 이유가 없으니 메모리를 해제한 후 NULL을 반환한다.
  3. 찾은 경우엔 남은 부분을 저장할 메모리를 새롭게 할당한다.
  4. ft_strlcpy 함수를 통해 새로 할당한 메모리에 \n 뒷부분을 복사한다.
    복사할 데이터가 없어 ft_strlcpy함수의 반환값이 0이었다면 BUFFER_SIZE만큼 읽어들였을 때 우연히 \n로 끝나거나, 혹은 읽어들인 파일 자체가 \n으로 끝나는 경우이다. 어느 쪽이든 remain에 남길 데이터가 없으므로 새로 할당한 메모리와 remain의 메모리를 해제 후 NULL을 반환한다.
  5. 복사한 데이터가 있다면 기존의 remain은 해제하고, 새로 만들어진 문자열을 반환한다.