본문 바로가기

프로그래밍/C

[utility] get_next_line 구현하기

📌 매뉴얼 (in subject)

더보기

Function name

     get_next_line

 

Prototype

     char *get_next_line(int fd);

 

Parameters

     fd: The file descriptor to read from

: 읽어온 파일 디스크립터

 

Return value

    Read line: correct behavior / NULL: there is nothing else to read, or an error occurred

: 올바른 동작은 줄 1줄 읽어오는 것이고, 읽을 게 없거나 에러가 발생하면 NULL을 반환한다

 

External functs

     read, malloc, free

 

Description

     Write a function that returns a line read from a file descriptor

: 파일 디스크립터로부터 읽어와 한줄을 반환하는 함수를 작성한다

 

📌 작성 코드 (1) - header file code

#ifndef GET_NEXT_LINE_H
# define GET_NEXT_LINE_H

# ifndef BUFFER_SIZE
#  define BUFFER_SIZE 10
# endif

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

size_t	do_strlen(const char *s);
char	*do_strdup_s(char *str, char *buff);
char	*do_strljoin(char *s1, char *s2, int rs);
size_t	do_strlcpy(char *dst, const char *src, size_t size);
char	*get_next_line(int fd);

#endif

📌 작성 코드 (2) - utils code

#include "get_next_line.h"

size_t	do_strlen(const char *s)
{
	size_t	i;

	i = 0;
	while (s[i] != '\0')
		i ++;
	return (i);
}

char	*do_strdup_s(char *str, char *buff)
{
	char	*dup;
	size_t	buff_len;
	size_t	i;

	buff_len = do_strlen(buff);
	i = 0;
	dup = (char *)malloc(sizeof(char) * (buff_len + 1));
	if (dup == NULL)
		return (NULL);
	while (buff[i] != '\0')
	{
		dup[i] = buff[i];
		i ++;
	}
	dup[i] = '\0';
	if (str != NULL)
		free (str);
	return (dup);
}

char	*do_strljoin(char *s1, char *s2, int rs)
{
	char	*s3;
	size_t	i;
	size_t	len_s1;

	i = 0;
	len_s1 = do_strlen(s1);
	if (!s1 || !s2)
		return (NULL);
	s3 = (char *)malloc(sizeof(char) * (len_s1 + rs + 1));
	if (s3 == NULL)
		return (NULL);
	while (i < len_s1)
	{
		s3[i] = s1[i];
		i ++;
	}
	while (i < len_s1 + rs)
	{
		s3[i] = s2[i - len_s1];
		i ++;
	}
	free (s1);
	s3[i] = '\0';
	return (s3);
}

size_t	do_strlcpy(char *dst, const char *src, size_t size)
{
	size_t	i;

	i = 0;
	while (i + 1 < size && src[i] != '\0')
	{
		dst[i] = src[i];
		i ++;
	}
	if (size != 0)
		dst[i] = '\0';
	return (do_strlen(src));
}

📌 작성 코드 (3) - main code

#include "get_next_line.h"

static int	read_and_store(int fd, char **str)
{
	char	buff[BUFFER_SIZE + 1];
	int		rs;

	rs = read(fd, buff, BUFFER_SIZE);
	if (rs <= 0)
		return (rs);
	buff[rs] = '\0';
	if (*str == NULL)
		*str = do_strdup_s(*str, buff);
	else
		*str = do_strljoin(*str, buff, rs);
	return (rs);
}

static char	*put_next_line(char **str, size_t len, size_t i)
{
	char	*restr;

	restr = NULL;
	i ++;
	restr = malloc(i + 1);
	if (restr == NULL)
		return (NULL);
	do_strlcpy(restr, *str, i + 1);
	if (i - 1 == len)
	{
		free (*str);
		*str = NULL;
	}
	else if (i - 1 != len)
		*str = do_strdup_s(*str, *str + i);
	return (restr);
}

char	*get_next_line(int fd)
{
	static char	*str = NULL;
	int			rs;
	size_t		len;
	size_t		i;

	while (1)
	{
		rs = read_and_store(fd, &str);
		if (str[fd] != NULL)
			len = do_strlen(str);
		if (rs == -1 || (rs == 0 && str == NULL) || (len == 0))
		{
			free (str);
			str = NULL;
			return (NULL);
		}
		i = 0;
		while (str[i] != '\n' && i < len)
			i ++;
		if ((i == len && (rs < BUFFER_SIZE)) || (i != len))
			return (put_next_line(&str, len, i));
	}
}

 

📌 코드 리뷰

- char *get_next_line(int fd)


동작 : 인자로 받아온 파일 디스크립터(fd)로 읽어온 파일에서, 개행을 찾아가며 1줄 씩 반환해주는 함수


주요 과정 : static으로 내부정적변수 str을 선언하고, while(1)을 통해 무한 반복문을 돌린다.
그 후, read_and_store 함수에서 BUFFER_SIZE 만큼 read하여 buff 문자열에 담아준다.
str이 비어있으면 (=NULL) strdup으로 새로 담아주고, 비어있지 않으면 strljoin으로 붙여준다.
그리고 읽은 read_size를 반환하여 해당 값에 따라 예외 처리를 해주고, 개행이 해당 str에 포함되어 있으면

put_next_line 함수를 실행한다. 해당 함수에서는 현재 str에서 개행 전까지 문자열은 반환하고,

개행 이후의 문자열만 str에 남겨두는 역할을 한다. 만약 위에서 str에 개행이 포함 되어있지 않다면,

while문을 다시 실행하여 다시 read_and_store 함수를 실행하여 BUFFER_SIZE만큼 read하는 과정을 반복하다

개행을 만나 반환할 때까지 수행한다.
 
설명
- 우선, static을 통해 함수 내에 만든 내부정적변수는 프로그램이 시작될 때 할당되고, 종료할 때 해제된다.
따라서 여러번 함수를 실행해도, 한번만 초기화되므로 이를 활용해 개행 이후의 문자열을 담아두는 역할을 한다.


- while문을 무한 반복으로 하여 계속 실행하게 되는데, 개행을 만나서 put_next_line 함수의 반환값을 반환하면

그때 종료된다. get_next_line 함수가 종료되어도 위에서의 언급처럼 내부정적변수이므로,

프로그램 종료까지 static이 유지가 되는데, 첫 줄이 개행을 만나 get_ next_line을 처음 반환했다고 해도,

static 변수에 그 개행 이후의 값이 남아있을 수 있다.

(Buffer 크기만큼 read를 반복하므로, buffer 크기에 따라 개행 이후의 문자열이 남아 있을 수 있다.)

 

- 이 static 으로 선언한 문자열에 개행이 중간에 있다면 그 이후의 문자열만 static변수에 남겨두는 과정이 필요하다.

다음 get_next_line을 불러왔을 때, static에 있는 문자열 뒤로 새로 읽어온 문자열을 붙여야 정상적으로

다음 줄을 가져올 수 있기 때문이다.
 

- 실행 시, buffer size를 터미널에서 표준입력으로 받아와 적용하는데, 이 입력값이 존재하지않을 때는

header에서 define이 되지 않았을 때 10으로 기본값을 지정해두었다.

 

- util들을 확인해보면, 기존 라이브러리에 만들어뒀던 str 관련 함수들과 다르게 가져온 string을 free해주는 과정을

추가해두었다. 기존에 존재하는 string을 free해주고 새로 할당된 문자열을 반환하는 작업 등을 하기위해 적용하였다.

 

회고
- 3 ~ 4달 전에 짠 함수라 지금 보니 정리가 깔끔하게 되어있지 않다는 느낌이 있긴 하다.

또한, 불필요한 부분이 존재하며, buffer만큼 계속 읽는 것보다는, 이미 str에 개행이 존재하면 읽지 않고 바로

put_next_line으로 넘겨주는 것이 좋을 것 같다. 아무래도 이런식으로 바꾸려면 새로 다 바꿔야할 것 같은데,

추후에 시간이 되면 'get_next_line_renewal_ver'로 새로 만들어봐야겠다. (...)

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

[function] pthread_join 함수 알아보기  (8) 2024.09.07
[function] pthread_create 함수 알아보기  (5) 2024.09.07
[graphic] miniLibX Manual  (0) 2024.08.06
[library] itoa 구현하기  (0) 2024.06.29
[library] split 구현하기  (0) 2024.06.29
Recent Posts
Popular Posts
Recent Comments