42Seoul
get_next_line.c
Lord DEVader
2022. 12. 17. 23:27
과제
Function name
|
get_next_line
|
Prototype
|
char *get_next_line(int fd); |
Parameters
|
fd : the file descriptor to read from
|
Return
|
Read line : correct behavior NULL : there is nothing else to read, or an error occurred |
Description
|
Write a function that returns a line read from a file descriptor |
External functs.
|
read, malloc, free |
지시사항
- get_next_lint()함수를 반복 호출해서 fd로부터 가리켜진 텍스트 파일을 읽어야함. 한번에 한줄씩
- 읽어진 줄을 리턴해야함.
- std input과 파일을 읽을때 둘 모두 정상 작동해야함.
- 리턴하는 라인은 \n으로 끝나야함.
다만, 파일의 끝까지 갔는데도, \n으로 끝나지 않은경우를 제외하고.
- 파일을 읽어야 하기 때문에, 컴파일러 콜에 -D BUFFER SIZE=n을 추가해줘야함.
read()함수를 위한 버퍼 사이즈를 정의해줌. 버퍼사이즈는 채점시 수정되며 검사할것임.
- read()함수가 파일의 끝에 닿지 못한 반면, Fd로부터 가리켜진 파일이 마지막 호출 이래로 바뀌면, 정의되지 않은 행동을 갖는것을 고려 하라
공부해야할 개념
- static
- static 키워드가 붙어있으면, 지역변수이면서 전역변수처럼 사용될수 있다. 따라서 함수 호출 사이에 값을 잃지 않는다.
- static 프로그램 실행 시 할당되고, 프로그램 종료시 파괴되는 변수
- 선언시 자동 0으로 초기화.
- 외부에서는 참조할수 없어서, 정보은닉효과가 있다.
- 함수 내부 정적변수는 다른 함수에서 참조할수 없다.
- 함수 내부 정적변수는 반복 호출 되어도, 다시 초기화 되지 않고, 계속 남아 있는다.
- read()
- unistd.h 라이브러리 이용
- ssize_t read(int fd, void *buf, size_t nbytes)
- 파라미터
- fd : 읽을 파일의 파일디스크립터
- buf : 읽어들인 내용을 저장할 버퍼
- nbytes : 읽어들일 데이터의 최대길이 ( buf의 크기보다 커서는 안됨).
- 반환
- -1 : 실패
- 0 : EOF을 만나면
- 0초과의 수 : 읽어들인 바이트 수
- 작동
- read()는 해당 함수가 호출될 때마다 파일 디스크립터(이하 fd)가 참조하는 파일의 현재 file offset에서 len 바이트만큼 buf로 읽어들인다.
- 읽어들이는 작업에 성공하면 buf에 쓴 바이트 숫자를 반환하고, 실패하면 이전 게시글에서 다루었던 open()처럼 -1을 반환하면서 errno를 설정한다.
- file offset은 fd에서 읽은 바이트 크기(len)만큼 앞으로 나아간다. 파일 디스크립터가 표현하는 객체에 탐색 기능이 없다면, 읽기 작업(read)는 항상 현재 위치에서 일어난다.
- ssize_t
- size_t는 32bit 환경 기준으로 unsigned int이지만, ssize_t는 signed int이다.
- 이를 통해 반환값으로 실패여부를 알수있다.
- fd
- : file descriptor
- 프로세스 내에서 열린 파일을 고유하게 나타내는 값.
- 0, 1, 2는 각각 stdin, stdout, stderr로 예약되어 있다.
- 로직
- 1. 버퍼를 채워주고, 정적 문자열 변수에 저장해준다. 만약 \n을 못 만났다면, 반복해준다.
- 2. 정적 문자열 변수에서, \n을 만난 이후만 남기고, 나머지는 반환해준다.
- 3. 반복된 호출에서도 정적 변수덕에 남아 있는 버퍼를 활용할수 있다.
- 구현
#include "get_next_line.h"
/*
read()함수 에러 발생시 호출
static 버퍼를 비워주고, 빈 문자열로 만들어줌.
*/
char *ft_read_err(char *stt_buffer)
{
char *temp2;
temp2 = stt_buffer;
stt_buffer = ft_strdup("");
free(temp2);
return (stt_buffer);
}
/*
fd에서 개행 문자를 찾을때까지 BUFFER_SIZE만큼 읽어준다.
*/
char *ft_read(int fd, char *buffer, char *stt_buffer)
{
int len;
char *temp;
len = 1;
while (len)
{
len = read(fd, buffer, BUFFER_SIZE);
//fd에서 버퍼사이즈 만큼 읽고, buffer에 저장, 읽을 글자스 리턴.
if (len < 0) //read()함수 오류인 경우.
{
stt_buffer = ft_read_err(stt_buffer);
//static 버퍼를 비워주고, 빈 문자열로 만듦
return (stt_buffer);
}
else if (len == 0) // EOF(end of file)
break ;
buffer[len] = '\0'; //null terminating
if (!stt_buffer)
//static 버퍼가 아직 할당이 안됬다면, 빈문자열로 초기화.
stt_buffer = ft_strdup("");
temp = stt_buffer;
stt_buffer = ft_strjoin(stt_buffer, buffer);
//기존 static 버퍼와, 이번에 읽어준 버퍼와 합쳐, 새로운 문자열 할당.
free(temp);
//기존의 static 버퍼 메모리 해제. (메모리 낭비 방지)
if (!stt_buffer)
return (NULL);
if (ft_strchr(buffer, '\n') != 0)
//읽어준 부분에 개행문자(\n)을 찾아주고, 없다면, 계속 반복한다.
break ;
}
return (stt_buffer);
}
/*
\n을 만난 이후를 다른 문자열에 저장해준다.
기존 문자열 또한 그 순간에서 멈추게 해준다.
*/
char *ft_slice(char *line)
{
int i;
char *str;
i = 0;
while (line[i] != '\n' && line[i] != '\0') // \n 또는 \0 을 만나기 전까지 전진 시켜줌.
i++;
if (line[i] == '\0') //\0 이라면, EOF이기에, 함수 종료
return (0);
str = ft_substr(line, i + 1, ft_strlen(line) - i);
//개행 문자 이후 부분을 새로운 문자열에 저장해준다.
if (!str)
return (NULL);
if (*str == '\0')
//read()에서 오류 발생시 빈문자열만 반환을 하는데, 이러한 경우에 대해
{
free (str);
//line = NULL;
return (NULL);
}
line[i + 1] = '\0';
//기존의 문자열은 개행 문자(\n)이후를 \0로 terminating
return (str);
}
/*
개행문자까지 반환하는 함수.
*/
char *get_next_line(int fd)
{
static char *stt_buffer;
//static은 함수가 종료 되어도, 사라지지 않는다. 프로그램이 끝날때까지 살아남아 있다.
char *line;
char *buffer;
if (fd < 0 || BUFFER_SIZE <= 0) //fd와 버퍼 사이즈 유효성 검사
return (NULL);
buffer = (char *)malloc(sizeof(char) * (BUFFER_SIZE + 1));
// 버퍼 할당. \0을 넣어주기 위해 +1 해줌
if(!buffer)
return (NULL);
line = ft_read(fd, buffer, stt_buffer); //개행문자를 찾을때까지 읽어준 문자열
free(buffer);
if (!line)
return (NULL);
stt_buffer = ft_slice(line);
//static 버퍼는 개행 문자 이후의 문자열을 담고, line은 개행 문자 이전을 담는다.
if (!stt_buffer && *line == '\0') //read()함수 에러인경우 처리해줌.
{
free(line);
return (NULL);
}
return (line); //개행 문자 이전까지의 부분을 리턴해준다.
}