42seoul:: get_next_line 구현 및 정리

jahlee·2023년 1월 24일
1

개인 공부

목록 보기
5/23

파일디스크립터란?

fd => 0 표준입력, 1 표준출력, 2 표준에러
임의로 파일을 읽어들이면 3번부터 배정받는다.

static varible(정적 변수)

static 변수는 함수내부에서도 사용이 가능하고 전역에서도 사용이 가능하다. 특이하게도 static변수는 따로 초기화 하지 않아도 0 으로 초기화된다. static 변수는 프로그램이 시작될때 할당되고 프로그램이 끝날때 파괴된다. 전역 static 변수의 경우 그 소스파일 내의 모든 함수에서 사용이 가능하지만 함수 내부에서 선언을 하게되면 다른 함수에서는 값을 참조할 수 없다. 이를 내부정적변수라 하는데 프로그램이 시작될때 한번만 초기화가 되어지고 반복실행된다하여도 초기화 되지 않는다.

#주의할점#
내부 정적변수를 사용하는 과정에서 중괄호 내에서만(선언한 해당 함수 내) 사용이 가능하기 때문에 외부적인 접근에 어려움이 있다.

read 함수

헤더: unistd.h
형태: ssize_t read (int fd, void buf, size_t nbytes)
인수: int fd 파일 디스크립터 void** 
buf 파일을 읽어 들일 버퍼 size_t nbytes 퍼버의 크기
반환: ssize_t == -1 실패> 0 정상적으로 실행되었다면 읽어들인 바이트 수

버퍼의 크기보다 파일이 크면 여러번 읽어올수있는데 read 함수가 자체적으로 이전에 읽은부분 이후를 버퍼에 담아준다.

    #include <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include "./get_next_line/get_next_line.h"
    
    int main()
    {
    	int fd;
    	static t_gnl_list	*head;
    	char			*buf;
    
    	if ( 0 < ( fd = open( "./txt1.txt", O_RDONLY))) //12345가 들어있는 파일
        {
          printf("%zd || %s\n",read( fd, buf, 2),buf);// 2 || 12
          printf("%zd || %s\n",read( fd, buf, 2),buf);// 2 || 34
          printf("%zd || %s\n",read( fd, buf, 2),buf);// 1 || 54
    	  /* 읽은 바이트수가 버퍼사이즈보다 작으면 끝까지 읽은 것이고, buf+바이트수 -1 까지 버퍼에 담긴것이다. */
          printf("%zd || %s\n",read( fd, buf, 2),buf);// 0 || 54 
          close( fd);
        }
        else
           printf( "파일 열기에 실패했습니다.\n");
        return 0;
    }

open 함수

형태 : open (const char *FILENAME, int FLAGS[, mode_t MODE])

파일 이름과 파일에 대한 열기 옵션을 주게 되면 파일 디스크립터의 양의 정수 값을 반환 하게 된다.

이 때 특이하게도 같은 파일을 두번 열면 같은 디스크립터가 아니라 다른 디스크립터가 반환된다.

구현시 주의할점들

  1. malloc 을 하고 strlcat와 같은 함수를 사용할때 기본적으로 malloc을 한 값에는 쓰레기 값이 들어있기때문에 맨처음 값에 ‘\0’을 넣어주는 편이 좋다. 또한 할당한 부분 넘어서 까지 참조를 하는 부분이 있는지에 대해 조심해주어야한다. 만약 할당한 부분을 넘어서 참조를 하면 heap overflow라던지 stack overflow 에러가 발생한다!!
  2. leak이 발생하지 않도록 malloc을 해주었던 부분들에 대해서 잘 인지하고 있어야 한다.
  3. malloc을 너무 많이하게되면 엄청 큰파일을 작은 버퍼로 읽었을때 시간이 오래걸릴수있다. 본인은 할당을 너무 크게하는 것도 leak라 판단을 하고 풀었지만 함수의 효율성을 위해서는 충분히 사용할수 있을법한 방법이다. 이와같은 할당방식을 c++ vector자료구조형에서 사용한다.
    간단하게 설명을하면 할당한 공간이 다 찼을거나 부족할 것 같을때 할당을 기존의 2배를 해 늘리는 방식과 같이 구현을 한다고 생각하면 된다.

구현 방식

기본적으로 양방향 리스트를 사용하여 여러 파일들에 대한 접근을 가능하게 하였다.

  1. fd 값이랑 동일한 값을 가지는 리스트 노드를 탐색한다. 없다면 static 변수를 통해 head를 고정시켜주고 임의의 tmp노드를 사용하여 리스트 구조체의 값들에 접근을 한다.

  2. 맨처음 해당 리스트 tmp에 backup이 존재한다면 nl의 존재여부에 대한 확인을 하고, 만약 nl이 존재한다면 nl까지 부분을 결과값으로 리턴해주고 나머지 부분은 다시 해당 노드 backup부분에 저장한다.

  3. backup이 없거나 nl이 존재하지 않는다면 while문을 통해 read를 하게 되고, read를 할때마다 nl의 존재를 판별해준다. nl이 존재한다면 backup + read nl까지 를 리턴해주고 나머지는 backup부분에 저장해준다. 만약 없다면 backup + read 한부분을 backup에 저장해주고 while문을 다시 돌게된다.

  4. while문의 종료 조건은 eof이거나 read 오류가 발생하였거나 malloc에 대한 오류가 발생하였을 때이다. 정상적으로 eof에 도달하게 된다면 backup에 존재하는 값()을 출력해주고 해당 리스트를 free해준다. 이외의 오류로 인한 종료 조건들의 경우 leak이 발생하지 않도록 malloc한 부분들을 전부 free해주고 해당 리스트의 노드도 free 해준다.

  5. 다 읽은 파일의 경우 read값이 0 이기때문에 이 점을 활용하여 NULL을 리턴하게끔 하면 된다.

구현 코드 예시

t_gnl_list	*del_gnl_list(t_gnl_list **tmp)
{
	/*
    	노드에 연결되어진 다른 리스트들에 대해 판별하고 리스트들을 이어주거나 하는
        작업을 해준다.
    	해당 노드내에 동적할당된 부분들을 free해주고 해당노드 또한 free해준다.
    */
}

t_gnl_list	*find_fd(t_gnl_list *tmp, int fd, t_gnl_list *head)
{
	/*
    	해당 fd와 같은 값을 가지고있는 리스트를 탐색하고 만약 없다면 새로운 리스트를 만들어준다.
        이후 해당 리스트의 주소값을 리턴해준다.
    */
}

char	*combine_all(char **str1, char **str2, t_gnl_list **tmp)
{
	/*
    	두 문자열을 합쳐주는 역할을 하는 함수. 합쳐주고 사용되어진 문자열들은 free해주고
        NULL을 가리키게끔 한다.
    */
}

char	*read_line(t_gnl_list **tmp, int fd)
{
	/*
		while문을 돌면서 read를 하고 eof를 판별해주며 결과에 맞는 값을 리턴해준다.
    */
}

char	*get_next_line(int fd)
{
	static t_gnl_list	*head; // 최초의 고정 노드
	t_gnl_list			*tmp; // 임의로 사용하는 노드
	char				*res;

	if (fd < 0)
		return (NULL);
	if (!head) // 최초 노드가 없다면
			head = find_fd(head, fd, NULL);
	if (!head)
		return (NULL);
	tmp = find_fd(head, fd, NULL);
	res = NULL;
	if (tmp->backup) // backup에 값이 존재한다면
		res = is_nl_backup(&tmp->backup, ft_len_free(tmp->backup, 0), tmp);
        /* backup에서의 nl존재여부에 따른 결과값을 받는다 */
	while (1)
	{
		if (!res) // 결과값이 없다면
			res = read_line(&tmp, fd); // read에서의 nl존재여부에 따른 결과값을 받는다
		if (tmp->eof || res) // eof거나 res값이 존재한다면
		{
			if (tmp->eof)
				head = del_gnl_list(&tmp); // eof면 해당 노드를 free해준다.
			return (res);
		}
	}
}

0개의 댓글