[Get_next_line] 함수 구현 및 로직

Cadet_42·2021년 6월 26일
0

Get_next_line

목록 보기
3/3

Subject: Get_Next_line

그동안 해왔던 libft주제와는 다른 형식의 주제였기 때문에 get_next_line 주제를 제대로 이해하는데만 시간이 다소 소요되었다. 하지만 주제를 한번 제대로 이해하면, 함수 구현을 하는데는 생각보다 시간이 오래 걸리지 않는다.

1. 문제 이해하기

int get_next_line(int fd, char **line);
  • 입력된 값을 '\n'으로 끊어서 매개변수 line에 저장하도록 구현.
  • 컴파일시, 상수 BUFFER_SIZE의 크기를 저장하도록 구현해야함.
    (가장 작은 상수부터 가장 큰 상수의 BUFFER_SIZE를 넣어도 함수가 구현되어야 한다.)
  • read() 함수를 사용하여, BUFFER_SIZE단위로 파일을 read해야함.
  • return value : ERROR시 -1, 계속 읽을시 1, 파일의 끝일시 (EOF) 0을 반환함.

free()를 사용하여, memory leaks나 segmentation fault 오류에 주의 해야함.

2.내가 구현한 함수의 기본 개념들

static char buf[BUFFER_SIZE + 1]
  • buf : Character array that you store all the characters you read.
  • static char buf [BUFFER_SIZE + 1] : BUFFER_SIZE에 +1을 해주는 이유는 문자열 마지막에 위치한 '\0'을 위해 해준다.
  • line : get_next_line의 파라미터 안에 존재하는 line은 main에 의해 쓰이며, fd안에 각 line을 읽게 한다.
  • cur : line을 malloc을 사용하면서 복사하면서, malloc할 다음 포지션을 이동한다.

3. 로직 및 알고리즘 구현

처음 구현한 알고리즘은 norm 에 맞지 않아, 하기의 코드를 2개의 function으로 나눴다.

1) ft_strchr(cur, '\n)을 찾지 못할때까지 while loop을 반복.
- cur내에 '\n'을 찾지 못할때까지, ft_strjoin()을 사용하여 이전 메모리와 이후의 메모리를 malloc해준다.
- free()를 사용함으로써, memory leaks를 방지한다.
- read_line < 0 : error를 방지한다.

2) 만약, 더이상 읽을 line이 없으면 (== 0 이면), while loop을 나가고, EOF(0)을 리턴해준다.

3) ft_strchr(cur, '\n') : 줄바꿈문자 '\n'을 찾을시,cur의 '\n' 자리에 0을 넣고 malloc을 해준다.
'\n'이 아닌 0을 넣어주면서, cur이 다음 line으로 이동 할 수 있도록 이동한다.

==> 이 경우, 리턴값은 1을 리턴해준다(아직 끝이 나지 않았으므로, 아직 읽어야할 line이 있으므로).

< 처음 구현한 get_next_line의 함수는 하기와 같다. >

#include "get_next_line.h"

int get_next_line(int fd, char **line)
{
    static char buf[BUFFER_SIZE + 1];
    static char *cur = NULL;
    long        read_line;
    char        *line_cpy;

    if (fd < 0 || BUFFER_SIZE <= 0 || !line)
    return (GNL_ERROR);
    if (!cur) // 1er fois => 처음을 정의 해 주어야함. 아니면, cur이 앞으로 나아가지 못함. 
    {
        read_line = read(fd, buf, BUFFER_SIZE);
        if (read_line < 0)
        return (GNL_ERROR);
        buf[read_line] = 0; // buf의 끝은 0. 
        cur = buf;
    }   
    *line = ft_strdup("");
    // *line = ft_strdup("") 해주는 이유 : 추후 while에 해당하는 첫번째 조건이나, 두번째 조건에 ft_strjoin을 사용하는데, 
    // 사용해주기 위해서는 메모리를 동적할당 해줄 첫번째 값이 필요함. 첫번째 값은 비어있으므로, ft_strdup("") 값을 넣어줌.  
    // cur : buf내에서  메모리를 동적할당하면서 (malloc), 이동함.

    while (!ft_strchr(cur, '\n')) // 첫번째 condition : '\n'을 찾지 못할때까지,while loop 반복하라.
    {
        line_cpy = ft_strdup(*line);
        free(*line);    // line을 free 해주어야 memory leaks이 발생하지 않는다. 
        *line = ft_strjoin(line_cpy, cur);
        free(line_cpy);
        read_line = read(fd, buf, BUFFER_SIZE);
        if (read_line < 0)
        return (GNL_ERROR);
        buf[read_line] = 0; //  buf 마지막을 = 0 으로 정의 해주어한다. 아닐경우, buf의 마지막이 언젠지 알지 못해 infinite loop이 발생한다. 
        cur = buf;
        if (!read_line) // if read_line == 0 끝이므로, while loop을 나간다. 
        return (GNL_EOF);
    }
    //두번째 condition : '\n'을 찾으면, cur의 '\n' 자리에 0을 넣고 malloc을 해준다. 
    *ft_strchr(cur, '\n') = '\0'; // cur의 '\n' 자리에 0을 넣는다. 
    line_cpy = ft_strdup(*line);
    free(*line); //supprimer line
    *line = ft_strjoin(line_cpy, cur);
    free(line_cpy);
    cur = ft_strchr(cur , '\0') + 1; // cur이 다음 line으로 이동한다.   
    return (GNL_SUCCESS);
}

4. Get_next_line_utils 함수

1. ft_strlen : 문자열 str의 길이를 구하는 함수.

  • 용도 : ft_strjoin 내의 내장 함수로 쓰임.
size_t	ft_strlen(const char *str)
{
	int	i;

	i = 0;
	while (str[i])
		i++;
	return (i);
}

2. ft_strjoin: malloc을 사용하여,인자로 받은 두 개의 문자열 s1, s2를 합쳐 반환하는 함수.

  • 처음엔, ft_strjoin 안에 free를 넣어 memory leaks를 잡아주는 방법을 생각했었다. 하지만, norm 때문에 (25라인을 넘으면 안됨) 그냥 libft에 사용했던 ft_strjoin 함수를 그대로 쓰기로 했다.
  • 용도 : get_next_line() 함수 내에서 malloc을 하면서 전의 문자열과 이후의 문자열을 합쳐 반환할때 쓰였다.
char	*ft_strjoin(char *s1, char *s2)
{
	char	*str;
	int		i;
	int		j;
	int		s1_len;
	int		s2_len;

	if (s1 == 0 || s2 == 0)
		return (NULL);
	i = 0;
	j = 0;
	s1_len = (int)ft_strlen(s1);
	s2_len = (int)ft_strlen(s2);
	str = malloc (s1_len + s2_len + 1);
	if (!str)
		return (0);
	while (i < s1_len)
	{
		str[i] = s1[i];
		i++;
	}
	while (j < s2_len)
		str[i++] = s2[j++];
	str[i] = '\0';
	return (str);
}

3.ft_strdup : malloc을 사용하여 문자열 s1를 복사하고 복사된 문자열을 가리키는 포인터를 반환하는 함수

  • 처음 ft_strjoin을 쓰기 전에, 메모리를 동적할당 해줄 첫번째 값이 필요한데, ft_strdup("")을 사용하여 첫번째 값으로 선언 및 정의 해주었다.
char	*ft_strdup(const char *s)
{
	size_t	len;
	size_t	i;
	char	*str;

	len = ft_strlen(s);
	str = malloc (len + 1);
	if (!str)
		return (NULL);
	i = 0;
	while (len--)
		str[i++] = *s++;
	str[i] = 0;
	return (str);
}

4. *ft_strchr : 문자 c를 검색할 문자열 str 함수.

  • !(ft_strchr(cur, '\n')) 와 *ft_strchr(cur, '\n') = '\0'의 두 조건을 사용할때 쓰였다.
char	*ft_strchr(const char *str, int c)
{
	int	i;

	i = 0;
	while(str[i])
	{
		if ((char)c == str[i])
		{
			return ((char *)(str + i));
		}
		i++;
	}
	if (c == 0)
		return ((char *)(str + i));
	return (0);
}

5. Get_next_line의 main 함수 및 컴파일 방법

int ac char *av[]을 사용하여 get_next_line의 메인을 만들었다.

하기와 같이 BUFFER_SIZE를 다르게 정의 하면서 컴파일을 해주었다.

gcc -Wall -Wextra -Werror -D BUFFER_SIZE=12 get_next_line.c get_next_line_utils.c
int main(int ac, char *av[])
{
    if (ac != 2)
    return (0);
    
    char *line;
    int fd = open(av[1], O_RDONLY);
    int gnl;
    while (1)
    {
        line = NULL;
        gnl = get_next_line(fd, &line);
        if (line)
        {
            printf("%d: %s\n", gnl, line);
            free(line);
        }
        if (!gnl)
        break;
    }
}

Get_Next_line에 사용된 테스터기 (8개의 테스터를 한번에 사용할수 있다.)

https://github.com/Nimon77/gnl-test

출처 :
https://velog.io/@pawer/getnextline
https://velog.io/@yeunjoo121/ftstrdup

profile
안녕하세요! 개발공부를 하고 있습니다. 감사히 배우겠습니다. ;)

0개의 댓글