파도가 칠 때는 서핑을

Soon as possible

Wait!

공부/42Seoul

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

hyeonhki 2021. 3. 17. 08:22
728x90

- 문제 설명

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

- 문제 이해를 위한 예시 1

  • 텍스트 파일 예시 : "helloo\nhihi\nhi"
  • BUFFER_SIZE = 3

get_next_line 함수의 첫 호출

  1. buf = hel 저장
  2. backup = hel
  3. backup에 개행문자를 확인하고 개행문자가 없으니 다시 read
  4. "loo" buf에 저장
  5. backup에 buf를 이어붙여서 저장 👉 "helloo"
  6. backup에 개행문자 확인 👉 개행문자 없음 👉 다시 read
  7. buf = \nhi 저장
  8. backup에 buf를 이어붙여서 저장 👉 "helloo\nhi"
  9. backup에 개행문자가 있는 지 확인
  10. 개행문자가 있으니 line에 개행문자 이전과 널을 포함한 문자열(helloo\0)을 저장
  11. backup에는 개행문자 이후인 helloo\n"hi"를 저장 👉 다음 gnl의 호출을 준비
  12. return (1) // A line has been read

get_next_line 함수의 두번째 호출
0. backup = hi 저장되어 있는 상태

  1. buf = hi\n 저장
  2. backup = hihi\n
  3. backup에 개행문자가 개행문자가 있는 지 확인
  4. 개행문자가 있으니 line에 개행문자 이전과 널을 포함한 문자열(hihi\0)을 저장
  5. backup이 가리키는건 개행 이후인 '\0'
  6. return (1) // A line has been read

get_next_line 함수의 세번째 호출
0. backup = '\0' 저장되어 있는 상태

  1. buf = hi, read는 2
  2. backup = hi
  3. backup에 개행이 있는지 확인 👉 개행 없음 👉 다시 read
  4. read의 결과 한 글자도 읽지 못함 👉 return 0

- 용어 및 문제 추가 설명

  • EOF : End Of File, 파일의 끝에 도달했을 때 언제나 특별한 값을 반환하도록 하는데 그 값을 EOF 라고 하며, 실제값은 -1 을 나타낸다.
  • -D BUFFER_SIZE=xx 플래그
    : gcc -d 플래그는 외부에서 #define을 정의한다. 즉, 버퍼 사이즈를 지정하는 컴파일 옵션
  • read : byte만큼 fd를 읽어 buf에 저장한다.
    읽어온 byte값을 반환하고, 실패시 -1을 반환한다. 파일을 끝(EOF)까지 읽었으면 0 반환.
  • size_t read(int fd, void *buf, size_t bytes)
  • 정적변수(Static 변수)
    • 함수 내부/외부에 따라 내부/외부 정적 변수로 구분
    • 프로그램이 시작될 때 할당되고 프로그램 종료되면 파괴된다.
    • 내부 정적 변수는 초기화가 한 번만 이루어지며, 다른 함수에서는 참조 할 수 없음
    • 정적 변수는 선언 시 자동으로 0 초기화

- 해결 방법 및 구성

✓ Static 변수의 사용이유

  • gnl 함수의 특성상, return 값이 0이 나올때까지. 즉, 파일이 끝날때까지 프로그램이 끝나지않고 반복문을 돌기에 backup에 저장된 문자열을 '\n'을 기준으로 개행 전은 line에 저장하며, 개행 이후의 문자열 주소를 backup에 다시 저장시켜줘야 한다.

✓ 백업에 남는 문자열을 잘 확인하자

  • BUFFER_SIZE에 따라서 혹은 연속된 개행문자 등에 의해 read의 반환값이 0(=파일을 끝까지 읽음)이 되어도 backup에는 문자열이 존재할 수 있다. 이러한 이유로 인해 while문 밖에서도 조건문을 통해 확인이 필요했다.

1. 예외처리

  • fd < 0
    : open 에 실패할 경우 fd는 음수를 반환함.
  • line == 0
    : 매개변수 line에 널포인터가 들어온 경우
  • BUFFER_SIZE <= 0
    : read를 할 수 없음
  • READ에 실패한 경우
    : 비정상적인 fd가 read 함수에 들어온 경우 음수를 반환하는데, 이 경우를 꼭 예외처리 해주어야 한다.

2. read 반복문

  • 조건 : 버퍼사이즈만큼 read를 한 후 반환 값이 1 이상일 때 작동
  • buf에 read된 문자가 저장되어 있으니 백업에 옮겨준다.
    • 백업안의 개행을 확인한다.
    • 개행이 있을 경우 👉 (4)로 가서 line과 backup 주소이동
    • 개행이 없을 경우 👉 반복문으로 돌아가 다시 read
  • 2.1 저장된 백업안의 개행을 확인하는 함수

3. read의 반환값이 0으로 반복문 종료

  • 백업에 개행을 확인 👉 (4)로 가서 split
  • 개행은 없지만 백업이 존재할 경우(=EOF) 👉 line에 저장해주고 return 0
  • 빈 파일이 들어올 경우를 대비한 처리

4. backup의 개행문자 이전 문자열을 line에 저장하고 backup의 후속조치를 하는 외부함수(split)

  • line에 개행문자 이전의 문자열을 저장
  • backup 안 개행문자 이후의 문자열을 backup에 다시 저장!

- 주의해야 할 점

  • 메모리 누수(MEMORY LEAK)
    : 버퍼와 백업의 동적할당 후 해제할 것
  • fd가 비정상적인 값이 들어온 경우 에러 처리
  • 빈 파일이 들어온 경우 👉 빈 문자열을 line에 저장 후 반환
  • - Bonus!
  • 전역 변수 한 개로 get_next_line 작성
  • 다중 file discriptor를 관리할 수 있게 만드는 것
    : 파일 디스크립터의 경우, 정수형 넘버링이 운영체제 등에 따라 다르다.
    각 백업의 정보를 잃지 않고 여러 디스크립터를 읽을 수 있도록 구성하는 것.
    이차원 배열을 이용하여 각각의 정보를 저장하도록하여 해결하였다.

- main 테스트 코드

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

    fd = open("test2", O_RDONLY);
    while ((ret = (get_next_line(fd, &line)) > 0))
    {
        printf("ret : %d\n", ret);
        printf("line : %s\n", line);
        free(line);
    }
    printf("ret : %d\n", ret);
    printf("Last : %s\n", line);
    free(line);
}
728x90

'공부 > 42Seoul' 카테고리의 다른 글

[2 Circle] ft_server 개념부터 구현까지  (0) 2021.03.30
[1 Circle] ft_printf 정리  (0) 2021.03.18
[1 Circle] netwhat  (0) 2021.03.05
[0 Circle] Libft  (0) 2021.03.05