[gnl] get_next_line - 배열
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함수의 작동과정은 다음과 같다.
- 인자로 받아온
fd값과, 컴파일시-D옵션으로 지정한 변수BUFFER_SIZE의 값을 체크한다.fd < 0이거나BUFFER_SIZE <= 0이라면 정상적인 입력이 아니므로NULL을 리턴한다. check_remain함수를 통해remain에 문자열이 남아있는지 체크한다.remain이 없다면NULL을 리턴한다.- 있다면
make_next_line함수를 통해remain에 남아있는 문자열에서\n까지의 한 줄을 읽어next_line변수에 저장한다. update_remain함수를 통해next_line으로 저장할 한 줄을 빼고 나머지를remain으로 저장한다.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 함수의 작동과정은 다음과 같다.
- 변수
buff를BUFFER_SIZE + 1(\0문자가 들어갈 공간)만큼 할당한다. remain이 없거나,remain에 남아있는 문자열에\n이 없을 경우 반복문으로 들어간다.read함수로fd에서BUFFER_SIZE바이트만큼 데이터를 읽어와buff에 저장한다.
이 때read함수의 반환값은 정상적으로 읽어들인 바이트의 크기가 된다.
이를read_idx변수에 저장해 0보다 작으면fd에 해당하는 파일을 끝까지 읽은것이므로 반복문을 빠져나와 파일 읽기를 멈춘다.- 그렇지 않으면 읽어낸 데이터를 다루기 위해
buff[read_idx], 즉buff의 마지막에\0을 넣어준다. - 만약
remain이 빈 상태였다면ft_strdup함수를 이용해remain에 새롭게 메모리를 할당하며buff를 복사해준다. remain에 남아있는 데이터가 있었다면ft_strjoin함수를 이용해remain뒤에buff를 붙인 새로운 문자열을 할당한다.- 이 때
ft_strjoin함수로 반환되는 문자열은 기존의remain과 다른 메모리를 가지고 있으므로, 메모리 누수를 피하기 위해서 기존remain의 메모리를 해제한 후 새롭게 만든 메모리로 연결시켜주어야 한다.
- 이 때
remain에서\n을 찾을 수 있을 때까지 위 과정을 반복한다. 물론 중간의 탈출조건으로 반복문을 빠져나올 수도 있다.- 역할이 끝난
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 함수의 작동과정은 다음과 같다.
remain을 읽으면서next_line에 할당할 길이를 잰다.\n가 있다면 거기서 멈추고, 없다면remain의 끝까지 잰다.\n가 없는 경우는 파일을 끝까지 읽은 것이다.- 잰 길이로
next_line에 메모리를 할당한다. ft_strlcpy함수를 이용해next_line에remain의 문자열을 길이만큼 복사하고 마지막에\0을 넣는다.- 만들어진
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 함수의 작동과정은 다음과 같다.
ft_strchr함수를 이용해remain에서\n의 위치를 찾아fix_remain변수에 저장한다.- 찾을 수 없다면(
fix_remain == NULL) 파일을 끝까지 읽은 경우이다. 더이상remain을 남길 이유가 없으니 메모리를 해제한 후NULL을 반환한다. - 찾은 경우엔 남은 부분을 저장할 메모리를 새롭게 할당한다.
ft_strlcpy함수를 통해 새로 할당한 메모리에\n뒷부분을 복사한다.
복사할 데이터가 없어ft_strlcpy함수의 반환값이 0이었다면BUFFER_SIZE만큼 읽어들였을 때 우연히\n로 끝나거나, 혹은 읽어들인 파일 자체가\n으로 끝나는 경우이다. 어느 쪽이든remain에 남길 데이터가 없으므로 새로 할당한 메모리와remain의 메모리를 해제 후NULL을 반환한다.- 복사한 데이터가 있다면 기존의
remain은 해제하고, 새로 만들어진 문자열을 반환한다.