42Seoul

[42Seoul][GetNextLine] Get Next Line - mandatory part [C]

목차
GetNextLine 과제를 위한 선수지식
- 파일 디스크립터(fd)
- read 함수
- gcc -d 플래그
- static 변수

GetNextLine
Mandatory part
-get_next_line
-ft_get_lstr
-ft_get_line
-ft_reset_lstr
-get_next_line_utils

 

 

GetNextLine 과제를 위한 선수지식

파일 디스크립터(fd)

  • 운영체제가 만든 파일 또는 소켓의 지칭을 편히 하기 위해서 부여된 숫자이다.
  • 기본적으로 파일 디스크립터는 정수형으로 차례로 넘버링 되고 0,1,2는 이미 할당되어 있어서 3부터 파일 디스크립터를 부여한다.
    0 : 표준입력 (Standard Input)
    1 : 표준출력 (Standard Output)
    2 : 표준에러 (Standard Error)

 

read 함수

size_t read(int fd, void *buf, size_t bytes)
  • bytes 수 만큼 fd를 읽어 buf에 저장한다.
  • 성공시, 읽어온 바이트 수를 리턴함.
    0 (EOF) 파일을 끝까지 읽었으면, 다음번에는 더 이상 읽을 바이트가 없으므로 0을 반환함.
    -1 : 실패시 -1을 반환

 

gcc -d 플래그

  • 외부에서 #define을 정의한다.
  • 이 문제에서 컴파일은 다음과 같이 진행된다.
 $ gcc -Wall -Wextra -Werror -D BUFFER_SIZE=32 get_next_line.c get_next_line_utils.c

 즉, BUFFER_SIZE를 컴파일할 때 직접 입력하여 정하게 된다.

 

static 변수

  • 데이터 영역에 저장되어 프로그램 종료 시 까지 남아있기 때문에, 다음 line을 읽을 시작 주소을 계속 저장할 수 있도록 backup 버퍼를 static 변수로 선언해야 한다.

 

 

GetNextLine

Mandatory part

  • get next line 함수를 호출하면 파일 디스크립터에서 사용할 수 있는 텍스트를 EOF가 올 때까지 한 번에 한 줄씩 읽을 수 있게 함수 작성
  • textfile 에서 개행을 기준으로 한 줄씩 읽게 만드는 함수를 만드는 문제
  • get_next_line_utils.c 에 gnl 이 동작할 때 필요한 함수들을 추가(libft 사용금지)
  • textfile 을 개행 문자를 기준으로 한 문장씩 읽어 들어와, 문자열 주소를 저장해준다. 개행 문자가 나올 때까지 계속 read!
  • 개행 문자 전 까지 문자열을 넣어주는 것

 

메인 로직

 해당 함수안에 있는 변수이지만 함수가 불러오기 전과 부르고 나서도 값이 소실되면 안 되기 때문에 데이터 영역에 계속 저장을 해둘 수 있게 static 변수를 사용합니다. 

 파일 디스크립터 값과 BUFFER_SIZE 값이 비정상적인 값이 들어오면 Null값 처리합니다.

 함수가 최초 시작되는 것이 아니라 그 후 시작되었다면 데이터 영역에 존재할 lstr 값을 가져옵니다. 그리고 해당 row의 line 값을 lstr에서 가져옵니다. 이 값이 반환되는 값입니다. (get_next_line)

 이후 lstr 값이 그 다음 값이 되도록 설정해둡니다.

char	*get_next_line(int fd)
{
	static char	*lstr;
	char		*line;

	if (fd < 0 || BUFFER_SIZE <= 0)
		return (0);
	lstr = ft_get_lstr(fd, lstr);
	if (!lstr)
		return (0);
	line = ft_get_line(lstr);
	lstr = ft_reset_lstr(lstr);
	return (line);
}

 

ft_get_lstr,

buffer 에서 값을 읽어와 lstr에 더해주는 함수입니다.

즉, 외부입력에서 값을 가져오는 함수

 

buffer에 BUFFER_SIZE 크기만큼 동적 할당해줍니다. 

read 함수는 끝까지 읽었으면 0을 반환해주고 읽기에 실패했으면 -1을 반환합니다. 나머지 값은 읽은 바이트 값을 반환합니다.

buffer에 BUFFER_SIZE 바이트만큼 읽어 들어갑니다.

 

while 문 안을 보게 되면, strchr 함수(해당 찾는 문자열이 없으면 0)와 result가 0이 아닌 조건을 동시에 만족하면 돌게끔 했습니다.

즉, 개행 문자를 발견하거나 모든 문자열을 다 읽었을 경우 while 문을 빠져나옵니다.

while 문 내부에서는, 파일 읽기에 실패했을 경우 buffer를 동적 할당해줍니다.

lstr에 buffer 값을 이어 붙입니다.

 

while 문을 나오면 buffer를 동적 할당 해제하고 lstr를 반환합니다.

char	*ft_get_lstr(int fd, char *lstr)
{
	char	*buffer;
	int		result;

	buffer = (char *)malloc(sizeof(char) * (BUFFER_SIZE + 1));
	if (buffer == 0)
		return (0);
	result = 1;
	while (ft_strchr(lstr, '\n') == 0 && result != 0)
	{
		result = read(fd, buffer, BUFFER_SIZE);
		if (result == -1)
		{
			free(buffer);
			buffer = 0;
			return (0);
		}
		buffer[result] = 0;
		lstr = ft_strjoin(lstr, buffer);
	}
	free(buffer);
	return (lstr);
}

 

ft_get_line,

lstr에서 개행을 기준으로 line을 뽑아내는 함수입니다.

개행 전까지 길이를 i에 넣고 그 값을 동적 할당에 쓰게 합니다. 그 값을 후에 쓰이게 다시 0으로 초기화합니다.

tmp에 2 만큼 더 동적 할당해주는 이유는 개행과 문자열의 끝을 알리는 '\0' 값인 (Null 값)을 넣기 위해 공간을 마련하기 위함입니다.

 

while 문 안의 조건을 보면, 해당 값이 Null 값이거나 개행이 아닐 때까지 tmp에 모두 넣어줍니다.

만약 마지막 값이 개행일 경우 tmp 에 개행 값을 넣어줍니다.

개행이던 아니던 tmp 마지막에 '\0' (Null) 값을 넣어줍니다.

 

함수 반환 값으로 tmp 값을 반환합니다.

char	*ft_get_line(char *lstr)
{
	char	*tmp;
	int		i;

	i = 0;
	if (lstr[i] == 0)
		return (0);
	while (lstr[i] != 0 && lstr[i] != '\n')
		i++;
	tmp = (char *)malloc(sizeof(char) * (i + 2));
	if (tmp == 0)
		return (0);
	i = 0;
	while (lstr[i] != 0 && lstr[i] != '\n')
	{
		tmp[i] = lstr[i];
		i++;
	}
	if (lstr[i] == '\n')
	{
		tmp[i] = lstr[i];
		i++;
	}
	tmp[i] = '\0';
	return (tmp);
}

 

ft_reset_lstr,

위 함수들로 개행 까지 다 읽고 line으로 반환되었기 때문에 이 함수는 그 개행 뒤에 값을 해당 lstr로 만드는 함수입니다. 

(lstr 값이 Null 값일 경우에는 동적 할당 해제)

 

개행을 만나기까지 인덱스를 세줍니다. (Null 값일 경우, 동적 할당 해제)

tmp 동적 할당에서 전체 lstr 길이에서 개행을 만나기까지의 길이를 빼줍니다.

(현재의 get_next_line 값이 나온 것을 제외하기 위함)

 

tmp에 이전 line 값을 제외한 이후 lstr 값을 모두 다 넣어주고 tmp를 반환합니다.

char	*ft_reset_lstr(char *lstr)
{
	char	*tmp;
	int		i;
	int		j;

	i = 0;
	while (lstr[i] != 0 && lstr[i] != '\n')
		i++;
	if (lstr[i] == 0)
	{
		free(lstr);
		lstr = 0;
		return (0);
	}
	tmp = (char *)malloc(sizeof(char) * (ft_strlen(lstr) - i + 1));
	if (tmp == 0)
		return (0);
	i++;
	j = 0;
	while (lstr[i] != 0)
		tmp[j++] = lstr[i++];
	tmp[j] = '\0';
	free(lstr);
	return (tmp);
}

 

get_next_line_utils,

이전에 구현했었던 함수들입니다. 위 get_next_line에서 쓰입니다.

char	*ft_strchr(const char *str, int c)
{
	size_t	i;

	i = 0;
	if (str == 0)
		return (0);
	while (str[i] != 0)
	{
		if (str[i] == c)
			return ((char *)&str[i]);
		i++;
	}
	if (str[i] == c)
		return ((char *)&str[i]);
	return (0);
}

size_t	ft_strlen(const char *str)
{
	size_t	i;
	size_t	cnt;

	i = 0;
	cnt = 0;
	while (str[i] != 0)
	{
		i++;
		cnt++;
	}
	return (cnt);
}

char	*ft_strjoin(char *s1, char *s2)
{
	char	*str;
	int		i;
	int		j;

	if (s1 == 0)
	{
		s1 = (char *)malloc(sizeof(char) * 1);
		s1[0] = '\0';
	}
	if (s1 == 0 || s2 == 0)
		return (0);
	i = -1;
	j = 0;
	str = malloc(sizeof(char) * ((int)ft_strlen(s1) + (int)ft_strlen(s2) + 1));
	if (str == 0)
		return (0);
	while (s1[++i] != 0)
		str[i] = s1[i];
	while (s2[j] != 0)
		str[i++] = s2[j++];
	str[i] = '\0';
	free(s1);
	s1 = 0;
	return (str);
}

 

 

 


Reference

https://velog.io/@hidaehyunlee/GetNextLine-%EC%82%BD%EC%A7%88%EC%9D%98-%EA%B8%B0%EB%A1%9D

 

[GetNextLine] 삽질의 기록

Get Next Line은 읽어온 파일의 한 줄을 반환하는 프로그램이다.

velog.io

 

https://hyeonhki.tistory.com/34

 

[1 Circle] get next line 설명 및 해결

- 문제 설명 get next line 함수를 호출하면 파일 디스크립터에서 사용할 수 있는 텍스트를 EOF가 올 때까지 한 번에 한 줄씩 읽을 수 있게 함수 작성 파일을 개행(newline)을 기준으로 한 줄씩 읽게 만

hyeonhki.tistory.com

 

https://velog.io/@tak_4242/Getnextline-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-read%ED%95%A8%EC%88%98

 

[Get_next_line] 기본 개념 - read()함수와 정적변수

Getnextline : read()함수 1. read()함수 ssize_t : signed size type의 약자로, 보통의 32bit machine에서는 간단히 말해 int다. read()함수란? bytes 수 만큼 fd를 읽어 buf에 저장한다. 리턴값 : 성공시, 읽어온 바이트 수

velog.io

 


 

'42Seoul' 카테고리의 다른 글

[42Seoul][Printf] ft_printf - mandatory part [C]  (0) 2022.04.17