📌 매뉴얼 (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 |