ecole42

get_next_line

get_next_line은 파일 디스크립터에서 한 줄씩 읽어오는 함수를 구현하는 프로젝트. 정적 변수를 활용하여 여러 파일을 동시에 처리할 수 있으며, 버퍼를 효율적으로 사용하여 파일을 읽는다

C

프로젝트 소개

get_next_line은 파일 디스크립터(fd)에서 한 줄씩 읽어오는 함수다. 이 함수는 여러 번 호출되어도 이전에 읽은 내용을 기억하고, 다음 줄을 반환한다. BUFFER_SIZE 매크로를 통해 읽기 버퍼 크기를 조절할 수 있다.

주요 특징

  • 정적 변수를 사용하여 여러 파일 디스크립터 처리
  • 버퍼 크기에 관계없이 한 줄씩 안전하게 읽기
  • 메모리 누수 방지 및 안전한 메모리 관리
  • 두 가지 구현 방식 제공 (문자열 기반, 링크드 리스트 기반)

구현 버전

이 프로젝트는 두 가지 버전의 구현을 제공한다:

Version 1 (기본 버전)

  • 파일: get_next_line.c, get_next_line_utils.c, get_next_line.h
  • 구조: 정적 문자열 캐시 사용
  • 특징: 단순하고 직관적인 구현

Version 2 (링크드 리스트 버전)

  • 파일: get_next_line_v2.c, get_next_line_utils_v2.c, get_next_line_v2.h
  • 구조: 링크드 리스트를 사용한 캐시 관리
  • 특징: 더 효율적인 메모리 관리

파일 구조

get_next_line/
├── get_next_line.h              # Version 1 헤더 파일
├── get_next_line.c              # Version 1 메인 함수
├── get_next_line_utils.c        # Version 1 유틸리티 함수
├── get_next_line_v2.h           # Version 2 헤더 파일
├── get_next_line_v2.c           # Version 2 메인 함수
├── get_next_line_utils_v2.c     # Version 2 유틸리티 함수
├── main.c                       # 테스트용 메인 함수
└── 42cursus_gnl_tests/         # 테스트 디렉토리

함수 설명

Version 1

get_next_line(int fd, char **line)

  • 설명: 파일 디스크립터에서 한 줄을 읽어오는 함수
  • 매개변수:
    • fd: 읽을 파일 디스크립터
    • line: 읽은 줄을 저장할 포인터의 주소
  • 반환값:
    • 1: 성공적으로 한 줄을 읽은 경우
    • 0: 파일의 끝에 도달한 경우 (EOF)
    • -1: 오류가 발생한 경우

유틸리티 함수

  • ft_strlen(const char *s): 문자열 길이를 반환하는 함수
  • ft_strjoin(char *s1, char *s2, ssize_t r_size): 두 문자열을 결합하는 함수
  • ft_substr(char const *s, unsigned int start, ssize_t len): 문자열의 일부를 추출하는 함수

Version 2

get_next_line(int fd, char **line)

  • 설명: 링크드 리스트를 사용하여 파일 디스크립터에서 한 줄을 읽어오는 함수
  • 매개변수: Version 1과 동일
  • 반환값: Version 1과 동일

유틸리티 함수

  • ft_strdup2(char *str, ssize_t len): 문자열을 복사하는 함수
  • lst_add(t_cache **cache, char *content): 링크드 리스트에 노드를 추가하는 함수
  • hasnl(char *s): 문자열에 개행 문자가 있는지 확인하는 함수
  • lst_hasnl(t_cache *cache): 링크드 리스트에 개행 문자가 있는지 확인하는 함수

컴파일 및 사용법

컴파일

Version 1을 사용하는 경우:

gcc -Wall -Wextra -Werror -D BUFFER_SIZE=42 get_next_line.c get_next_line_utils.c main.c -o gnl

Version 2를 사용하는 경우:

gcc -Wall -Wextra -Werror -D BUFFER_SIZE=42 get_next_line_v2.c get_next_line_utils_v2.c main.c -o gnl

사용 예시

#include "get_next_line.h"
#include <fcntl.h>
#include <stdio.h>

int main(void)
{
    int fd;
    char *line;
    int ret;

    fd = open("test.txt", O_RDONLY);
    if (fd < 0)
        return (1);

    while ((ret = get_next_line(fd, &line)) > 0)
    {
        printf("%s\n", line);
        free(line);
    }

    if (ret == 0)
        printf("%s\n", line);  // 마지막 줄 출력
    free(line);
    close(fd);
    return (0);
}

표준 입력에서 읽기

./gnl
# 또는
echo -e "line1\nline2\nline3" | ./gnl

동작 원리

Version 1 (문자열 기반)

  1. 초기화: 정적 변수 cache를 빈 문자열로 초기화
  2. 읽기: read() 함수로 BUFFER_SIZE만큼 읽어서 버퍼에 저장
  3. 결합: 읽은 내용을 기존 cache와 결합
  4. 개행 확인: cache에 개행 문자(\n)가 있는지 확인
  5. 추출: 개행 문자가 있으면 그 전까지를 line에 저장하고, 나머지는 cache에 유지
  6. 반복: 파일의 끝까지 반복

Version 2 (링크드 리스트 기반)

  1. 초기화: 정적 링크드 리스트 cache를 NULL로 초기화
  2. 읽기: read() 함수로 읽은 내용을 링크드 리스트 노드로 추가
  3. 개행 확인: 링크드 리스트를 순회하며 개행 문자 확인
  4. 추출: 개행 문자 전까지의 모든 노드 내용을 line에 결합
  5. 정리: 사용한 노드는 제거하고, 개행 문자 이후 내용은 첫 노드에 재배치
  6. 반복: 파일의 끝까지 반복

메모리 관리

  • 각 버전은 메모리 누수를 방지하기 위해 사용한 메모리를 적절히 해제한다
  • line에 할당된 메모리는 호출자가 free()로 해제해야 한다
  • 정적 변수는 프로그램 종료 시까지 유지되며, 여러 파일 디스크립터를 처리할 수 있다

주의사항

  • BUFFER_SIZE는 컴파일 시 -D BUFFER_SIZE=n 옵션으로 정의해야 한다
  • line에 할당된 메모리는 사용 후 반드시 free()로 해제해야 한다
  • 여러 파일 디스크립터를 동시에 처리할 수 있지만, 현재 구현은 단일 정적 변수를 사용한다
  • 파일 디스크립터가 유효하지 않거나 line이 NULL인 경우 오류를 반환한다

테스트

프로젝트에는 42cursus_gnl_tests 디렉토리가 포함되어 있어 다양한 테스트 케이스를 실행할 수 있다.