1. get_next_line

zinnnn37·2022년 9월 27일
0

🪐 42 SEOUL

목록 보기
3/10

읽어온 파일을 개행문자 기준으로 한 줄씩 반환하는 함수


1.0. 사전 지식

1.0.0. File Descriptor

  • Unix 시스템에서 프로세스가 열린 파일들에 접근할 때 사용하는 개념으로, 열린 파일을 가리키는 고유한 값이다.
    • Unix 시스템에서는 파일뿐만 아니라 디렉토리, 소켓 등 모든 객체들을 파일로써 관리한다.
  • File descriptor음이 아닌 정수로, OPEN_MAX까지 존재한다.
    • OPEN_MAX는 단일 프로그램에 허용되는 열린 파일의 최대 개수를 정의하는 상수이다.
    • Unix 시스템에서 C언어의 OPEN_MAXlimits.h에 정의되어 있다.
  • 3가지의 Default input/output streams
    1. 표준 입력(Standard input: STDIN) = 0
    2. 표준 출력(Standard output: STDOUT) = 1
    3. 표준 오류(Standard error(STDERR) = 2
  • 프로세스가 실행 중인 파일을 연다면 커널은 해당 프로세스의 File descriptor 중에서 사용하지 않는 가장 작은 값을 할당한다.

1.0.1. static 변수

  • 정적으로 할당되는 변수이다.
  • 프로그램 실행 전반에 걸쳐 변수의 수명이 유지된다.
    • main이 아닌 함수에서 변수를 선언하는 경우 해당 함수가 종료되어도 변수가 해제되지 않는다.
#include <stdio.h>

void func() {
	static int x = 0;
	x++;
	printf("%d\n", x);
}

int main() {
	func(); // 출력값: 1
	func(); // 출력값: 2
	func(); // 출력값: 3
	func(); // 출력값: 4
	func(); // 출력값: 5
	return 0;
}
  • 위처럼 func 함수가 종료된 후 다시 호출되어도 x0으로 초기화되지 않고 이전의 값을 가지고 있다.
  • 단, static 변수, 전역변수, 함수는 정의된 C파일이 아닌 외부에서는 사용할 수 없다.

1.0.2. read()

   ssize_t read(int fd, void *buf, size_t count)
  • File Descriptor fdcount만큼 읽어 buf에 저장하는 함수이다.
  • 읽기가 정상적으로 진행되는 경우 읽은 바이트 수를, 읽기에 실패했다면 -1, EOF(End Of File)에 도달했다면 0을 반환한다.
	read(fd, NULL, 0);
  • 위 코드로 File Descriptor의 유효성을 검사할 수 있다. 파일에서 문자를 읽어오지는 않는다.

1.0.3. gcc -D

  • 컴파일 할 때 매크로를 정의할 수 있는 플래그이다.
  • gcc -D name=definition의 형태로 사용된다
    • name에 원하는 변수명을, definition에 값을 적고 컴파일을 하면 코드를 실행하기 전 name을 미리 정의한 후 값을 할당한다.
  • get_next_line에서는 gcc -Wall -Wextra -Werror -D BUFFER_SIZE=xx <files>.c로 사용된다.

1.1. Mandatory Part

1.1.0. char *get_next_line(int fd)

  1. 함수 종료 이후에도 읽은 문자열을 잃어버리지 않도록 static을 활용
    • linked list의 첫 번째 노드가 static, 함수가 종료되어도 head에 접근 가능케 함
    • 동적으로 할당한 메모리는 함수 종료 시 접근이 가능한 변수가 사라지게 되어 접근이 불가한 것이지, 메모리 자체는 존재
    • 때문에 head만 살아있다면 모든 노드에 접근 가능함
  2. 파일을 읽을 수 있는지 확인
    • File descriptor는 음이 아닌 정수
    • BUFFER_SIZE가 0 이하일 경우 문자를 버퍼에 저장할 수 없음
    • read(fd, NULL, 0)으로 유효한 fd인지 확인하는 것은 read_file()에서도 확인 가능하기에 굳이 넣지 않음
  3. linked list에서 현재 입력받은 fd 값을 가진 노드를 가져옴
  4. 파일 읽기 후 한 줄 반환

⚠️ bufline 할당 실패 시 ft_lst_del_noed()로 현재 노드를 해제하고 NULL 반환 ⚠️

1.1.1. t_list *get_fd(t_list **head, int fd)

  1. Mandatory Part의 경우 한 개의 fd만 다룸
    • 노드가 존재한다면 그 노드가 저장한 fd는 입력받은 fd와 동일하므로 해당 노드를 반환
  2. 빈 연결 리스트인 경우 새로운 노드 생성
    • 노드 내부의 buf는 빈 문자열로 초기화
    • 할당 실패 시 새로 생성한 노드의 메모리를 먼저 해제하고 NULL 반환
  3. 새로운 노드를 성공적으로 생성했다면 리스트의 처음에 추가
  4. 새로 생성한 노드 반환

1.1.2. char *read_file(t_list **head, t_list *cur, char *buf)

  1. buf는 파일을 읽어서 얻은 문자열을 임시로 저장하는 변수
    • norminette 규정을 맞추기 위해 read_file() 밖에서 할당 및 해제를 진행
  2. cur->buf\n이 존재하면 파일 읽기를 진행하지 않고 한 줄 반환을 진행
    • next 변수에 저장하여 get_line() 함수에 전달
  3. 없다면 파일을 읽기 진행
    • 기존에 저장한 문자열과 읽어들인 문자열을 strjoin을 이용해 합친 후 cur->buf에 새로 저장
  4. 새로 저장한 문자열에 \n이 있거나, 파일을 끝까지 읽은 경우(byte < BUFFER_SIZE) 한 줄 반환

1.1.3. char *get_line(t_list **head, t_list *cur, char *next)

  1. nextNULL일 때
    • 저장된 문자열이 빈 문자열이라면 NULL 반환
    • 저장된 문자열이 빈 문자열이 아니라면 line에 복사
  2. nextNULL이 아닐 때
    • 개행까지만 line에 복사
  3. save_next() 함수 호출
    • 반환값이 0이라면 오류 -> line 해제 후 NULL 반환
    • 반환값이 1이라면 line 반환

1.1.4. int save_next(t_list **head, t_list *cur, char *next)

  1. nextNULL일 때
    • 빈 문자열 저장
  2. nextNULL이 아닐 때
    • cur-buf의 개행 다음부터 끝까지 저장
  3. 1.과 2.에서 문자열 할당 실패 시 0 반환
  4. 1.혹은 2.의 결과를 cur->buf에 저장
  5. cur-buf가 빈 문자열인 경우 파일의 끝까지 읽었다는 의미이므로 ft_lst_del_node()를 호출, 현재 노드 삭제
  6. 1 반환

1.1.5. char *ft_lst_del_node(t_list **head, t_list *cur)

  1. 삭제할 노드가 첫 번째 노드인 경우 첫 번째 노드를 현재 노드 다음 노드로 변경
  2. 삭제할 노드가 중간에 있는 경우 삭제할 노드의 양 옆 노드를 서로 연결
  3. 삭제할 노드의 buf를 먼저 해제한 후 노드 자체의 메모리 해제


1.2. Bonus Part

get_next_line()은 메모리의 적절한 할당 및 해제가 중요한 과제라고 생각하여 이차원 배열로 구현하려 했으나, OPEN_MAX 파훼법이 여럿 존재한다는 점에서 안전한 디펜스를 위해 linked list로 구현함

get_fd() 함수 외에는 모두 동일

1.2.0. t_list *get_fd(t_list **head, int fd)

  • 여러개의 fd를 다루어야 하기 때문에, 입력 받은 fd 값을 가진 노드를 찾는 과정이 추가됨. 이 외에는 Mandatory Part와 동일

0개의 댓글