-
42Seoul - GNL(get_next_line)42Seoul 2021. 5. 18. 21:23반응형
이번 프로젝트는 파일을 읽어오기 위한 함수를 작성한다.
GNL함수는 파일의 한 줄을 읽어오는 것이다. "\n"이 나오기 전까지 읽는다.
따라서 이것을 여러 번 사용하여 한 파일을 끝까지 읽어오는 역할을 한다.
프로토타입 int get_next_line(int fd, char **line); 리턴 값 1 : 한 라인이 읽혔을 때.
0: EOF에 도달했을 때
-1 : 에러가 발생했을 때외부 함수 read, malloc, free * EOF(End of file)
매개 변수로는 fd(file descriptor)와 이중 포인터가 있다. fd는 main문에서 open함수를 이용하면 파일을 가리키는 정수를 한 개 반환해준다. 그리고 왜 이중 포인터를 사용하는지 궁금했다. 단일 포인터도 상관없는데... 이유는 main문에 char *str이라는 단일 포인터를 선언해줘야하기 때문이다. 그래서 str의 내용을 다른 함수에서 편집하려면 str의 주솟값을 조작해야하기 때문에 이중 포인터를 사용한다.
int fd = open("test.txt", O_RDONLY);
char *str;
get_next_line(fd, &str);
printf("%s\n", str);
get_next_line(fd, &str);
printf("%s\n", str);
get_next_line(fd, &str);
printf("%s\n", str);
get_next_line(fd, &str);
printf("%s\n", str);
위의 내용은 main문에서 어떻게 써야하는지 예시이다. 저렇게 하면 한 줄 씩 읽힌다.
그러나 문제가 있다. 함수를 저렇게 여러 번 호출하는 경우 어떻게 전에 읽었던 내용을 기억할까?
여기서 static variable(정적변수)의 개념을 알아보자.
메모리 구조 1. 코드 영역
프로그램 실행을 위해 코드가 저장되는 영역.
2. 데이터 영역
전역 변수, 정적 변수가 저장되는 영역. 프로그램 시작 시 할당, 종료 시 소멸.
3. 스택 영역
지역 변수, 매개 변수가 저장되는 영역. 함수 호출 시 할당, 종료 시 소멸. 높은 주소 -> 낮은 주소로 할당.
4. 힙 영역
사용자가 관리(malloc, free) 할 수 있는 메모리 영역. 낮은 주소 -> 높은 주소로 할당.
위와 같이 스택과 힙 영역은 같은 공간을 공유하고 있다. 그러나 데이터 영역에 선언을 하면 프로그램 종료까지 계속 데이터를 기억하고 있다. 그래서 우리는 이러한 특징을 가진 정적변수를 이용해 볼 것이다.
그리고 또 하나의 개념은 파일 디스크립트(fd)이다.
파일 디스크립트(fd)는 open, read함수로 파일들을 읽어 들일 때 필요하다.
우리가 얻은 0,1,2,3...숫자들은 file deescriptor table의 인덱스이다.
그리고 3번째 인덱스가 가리키는 file table로 가서 원하는 행동을 한다(mode지정, offset지정).
마지막로 inode table은 파일들에 대한 정보(접근 모드, 파일 형태, 고유 번호 등)을 지니고 있다.
그래서 우리는 static과 file desecriptor를 이용하여 파일을 관리 할 것이다.
이제 코드를 한 번 리뷰해보자.
int get_next_line(int fd, char **line) { ssize_t read_index; static char *str[OPEN_MAX]; char buf[BUFFER_SIZE + 1]; int flag; if (fd < 0 || !line || BUFFER_SIZE <= 0) //예외적인 부분 처리. return (-1); while ((read_index = read(fd, buf, BUFFER_SIZE)) > 0) //read가 잘 읽히는 경우. { if (str[fd] == 0) str[fd] = ft_strdup(""); //처음 시작 할 경우 NULL로 되어있어 공간을 하나 할당. buf[read_index] = '\0'; //BUFFER_SIZE만큼 읽고 제일 뒤에 NULL str[fd] = ft_strjoin(str[fd], buf); flag = ft_newline(str[fd]); if (flag >= 0) return (ft_split_line(line, &str[fd], flag)); } if (read_index < 0) return (-1); return (ft_return(&str[fd], line)); }
get_next_line함수의 내부모습이다. 일단 변수에 대해 설명하겠다.
* read_index = read함수의 반환 값을 받는 변수(-1 ~ BUFFER_SIZE)
* *str[OPEN_MAX] = fd table. 이중 포인터이고 static변수이다. 그리고 OPEN_MAX는 프로세싱 중 몇 개의 파일을 읽 을 수 있는가를 나타내는 변수.
* buf[BUFFER_SIZE + 1] = BUFFER_SIZE만큼 읽으면 읽은 것을 저장하는 변수.
* flag = "/n"의 유무와 str[fd]에서 어디에 위치해있는지 알 수 있는 변수.
--> 위 함수 중에서 str[fd] = ft_strdup("") 이것의 뜻은 원래 00의 주소값이 들어 간 것을 공간을 할당해서 0이 아닌 다른 주소값을 가르키게 하고 그 곳에는 NULL을 넣어놓은 상태이다.
int ft_split_line(char **line, char **static_str, int flag) { char *temp; int len; (*static_str)[flag] = 0; len = ft_strlen(*static_str + flag + 1); *line = ft_strdup(*static_str); if (len == 0) 개행뒤에 아무것도 없을 경우 { free(*static_str); *static_str = 0; return (1); } temp = ft_strdup(*static_str + flag + 1); //개행 뒤 문자들 복사 free(*static_str); //할당해제 *static_str = temp; // 개행 뒷 부분 넣어주기 return (1); }
ft_split_line 함수이다. 위에서 *static_str = str[fd]이다. 그리고 flag = (str[fd]에서 개행의 위치) 이다.
이중 포인터로 구성 되어있기 떄문에 free를 두 번 해준다. 그리고 static_str을 비워주고 개행 기준 뒤에 있는 문자들을 다시 할당해주고 복사한다.
int ft_return(char **static_str, char **line) { int flag; if (*static_str && (flag = ft_newline(*static_str)) >= 0) //정상적인 상황(EOF면서 개행일 때) return (ft_split_line(line, static_str, flag)); else if (*static_str) //개행 없이 EOF일 경우 { *line = *static_str; *static_str = 0; return (0); } *line = ft_strdup(""); //아무것도 안 적혀있으면서 EOF일 때 return (0); }
이 함수는 ft_return함수이다. 이것은 개행이 없거나 개행이랑 EOF가 겹치는 경우를 처리하기 위해 만든 함수이다.
이렇게 GNL을 완성하였다. 정적 변수와 프로세싱 중 메모리의 할당 및 구조등을 자세하게 이해 할 수 있었다. 남들보다 조금 오래걸렸지만 얻은 게 많은 프로젝트였다.
반응형'42Seoul' 카테고리의 다른 글
42Seoul - Push_swap (0) 2021.09.13 42Seoul - Minitalk (0) 2021.08.05 42Seoul - Netwhat (0) 2021.05.26 42Seoul - Libft (0) 2021.05.06 42Seoul본 과정에 앞서 피신 후기..!! (0) 2021.05.02