해당 함수를 반복문에서 호출하면, fd의 텍스트를 EOF가 올 때 까지 한번에 한 줄 씩 읽을 수 있는 코드를 작성하는 프로젝트입니다.
함수를 처음 호출했을 때 지정한 버퍼의 크기가 매우 커서 한번에 파일을 끝까지 읽었다 하더라도, 두번째 호출 했을때는 두번째 줄 부터 읽기 시작해야 합니다.
file, redirection, stdin으로부터 읽었을 때 함수가 제대로 작동해야합니다.
GNL 프로젝트는 open함수로 파일을 열었을 때 받는 파일 디스크럽터(fd)를 사용하여 한 줄씩 line에 저장하는 함수를 구현하면 된다고 할 수 있습니다. (정확한 의미로는 line의 포인터가 한 줄을 가리키게 만들어야 한다는 뜻)
이전에 작성한 사전지식 fd, 정적변수 등을 공부하고 오면 좋습니다.
char *get_next_line(int fd)
{
char buf[BUFFER_SIZE + 1];
char *line;
char *temp2;
static char *storage;
if (fd < 0 || BUFFER_SIZE < 1)
return (NULL);
line = line_maker(fd, buf, storage);
if (line == NULL)
return (NULL);
storage = namuji(line);
temp2 = gnl_strdup(line);
free(line);
line = NULL;
return (temp2);
}
동작하는 큰 틀은 텍스트를 static변수
에 저장한 다음 해당 저장내용에 '\n'
개행문자가 있는지 확인하고, 개행문자를 기준으로 개행문자 전까진 출력하고 나머지는 storage
에 계속 가지고 있으며 다음 반복문때 다시 접근하여 해당 동작을 파일의 EOF가 나올때까지 반복하는 함수입니다.
size_t read(int fd, void *buf, size_t bytes)
char *line_maker(int fd, char *buf, char *storage)
{
int r_size;
r_size = read(fd, buf, BUFFER_SIZE);
while (r_size > 0)
{
buf[r_size] = '\0';
if (!storage)
storage = gnl_strdup(buf);
else
storage = gnl_strjoin(storage, buf);
if (storage == NULL)
return (NULL);
if (gnl_check(storage) != -1)
return (storage);
r_size = read(fd, buf, BUFFER_SIZE);
}
return (storage);
}
버퍼 사이즈만큼 읽은것을 buf 변수에 넣고, 해당 문자열을 storage
에 계속 넣어줍니다.
그때마다 storage
에 '\n'
개행이 있으면 storage
를 리턴하고 앞뒤로 잘라 출력해줍니다.
#include "get_next_line.h"
#include <stdio.h>
int main(void)
{
char *line;
int fd;
fd = open("./test.txt", O_RDONLY);
while(1)
{
line = get_next_line(fd);
if (line < 1)
break ;
printf("line : %s\n",line);
}
return (0);
}
간단하게 출력하는 main문을 짜본다면,
test.txt
파일을 open
하여 나온 리턴값을 get_next_line()
함수 인자로 넣으면, 해당 txt 파일에 개행을 만나기 전까지의 문자열을 출력합니다.
char *get_next_line(int fd)
{
char buf[BUFFER_SIZE + 1];
char *line;
char *temp2;
static char *storage[OPEN_MAX];
if (fd < 0 || fd > OPEN_MAX || BUFFER_SIZE < 1)
return (NULL);
line = line_maker(fd, buf, storage[fd]);
if (line == 0)
return (NULL);
storage[fd] = namuji(line);
temp2 = gnl_strdup(line);
free(line);
line = NULL;
return (temp2);
}
보너스는 fd를 활용하여 각각의 fd값마다 다른 문자열 배열에 저장하는 방식입니다.
이전 포스트와 해당 포스트를 통해 알게된 점은
흔히 리눅스
시스템에서 사용하는 모든 것은 파일
이고, 일반적인 파일(Regular File)
에서부터 디렉토리(Directory)
, 소켓(Socket)
, 파이프(PIPE)
등등 모든 객체들은 파일로써 관리한다는 걸 알게 되었고, 프로세스가 이 파일들을 접근할 때에 파일 디스크립터(fd)
라는 개념을 이용한다는걸 배웠습니다.
또한, 프로그램이 운영체제로부터 할당받는 대표적인 메모리 공간
은 코드
, 데이터
, 힙
, 스택
영역이 있다는 사실과
일반적으로 사용하는 지역변수
는 스택영역
// malloc
, calloc
, new
와 같은 동적할당한 변수는 힙영역
// static 정적변수
는 데이터 영역
이라는 메모리 영역에 대해서도 깊게 공부하는 계기가 되었습니다.
코드를 작성하면서는 동적할당을 포함하는 strdup
, strjoin
등을 많이 활용하였는데 그에따른 동적할당 해제를 하지않아 메모리 누수가 나는 부분에 대해서도 많은 공부가 되었습니다.