Linux Programmer

C언어: fscanf를 대체하는 getline 함수 본문

컴퓨터 관련/C언어

C언어: fscanf를 대체하는 getline 함수

sunyzero 2012. 7. 18. 22:07

* 주의: 여기서 설명하는 getline은 C++의 getline 메소드와는 다른 것입니다. 혼동하지 마시기 바랍니다.


fscanf는 기본적인 입력 받는 함수로 모든 C언어 기초 책에서 소개되고 있지만 개행 문자(new line)나 공백 처리에 곤란한 부분이 있어서 실무환경에서는 fgets + sscanf 로 대체하여 사용하는 편이다. 


하지만 scanf는 여전히 형식화된(formatted) 데이터, 즉 숫자나 문자, 공백으로 띄워진 문자열들을 처리할 때는 편리하다는 장점도 있다. 그렇다고해서 scanf의 여러가지 문제점이 완전 해결되는 것은 아니지만!!



그래서 새로운 입력 문자열 처리 함수인 getlinegetdelim가 도입되었다. 이 두 함수는 2008년도 유닉스 표준인 SUSv4에서 처음 표준으로 확정되었다. 참고로 원래 glibc 의 GNU 확장에서 사용되던 함수였는데 기능이 좋아서 표준안에 반영된 것이다.


* 주의 할 점!!!

getline, getdelim은 유닉스 표준에 속한 함수이므로 POSIX 표준을 지원하는 시스템의 경우에만 사용할 수 있습니다. 혹은 구형 유닉스(2008년도 이전 제품?)에서도 지원하지 않을 가능성이 있습니다.



getline은 문자열을 행 단위로 읽어들이는 함수이고 getdelim은 구분자 단위로 짤라내는 기능의 함수이다. 먼저 두 함수의 프로토타입부터 살펴보자.

#include <stdio.h>

    ssize_t getline(char **lineptr, size_t *n, FILE *stream);
    ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream);

  Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
    getline(), getdelim():
        Since glibc 2.10:
            _POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700
        Before glibc 2.10:
            _GNU_SOURCE


이들 함수를 사용하려면 표준안 테스크 매크로인 _POSIX_C_SOURCE >= 200809L 혹은 _XOPEN_SOURCE >= 700으로 선언되어야 한다. 

혹은 glibc 2.10에서는 _GNU_SOURCE가 선언되어야 한다. 만일 위 매크로 조건이 맞지 않으면 문법 검사에서 에러가 발생할 수 있다.


1. getline

getline은 개행 문자를 기준으로 한 행을 읽어들인다. 함수 원형에 있는 *lineptr은 문자열을 저장할 버퍼 공간으로 크기는 n에 입력해서 넣어준다. 


*lineptr에는 미리 malloc으로 할당해두어도 되지만 NULL을 지정하면 getline 내부에서 malloc을 호출하여 메모리를 할당해준다. 이왕이면 내부에서 할당하도록 하는 편이 좋다.

그리고 편리한 점은 한 행을 읽어들이는데 버퍼 메모리가 부족하면 알아서 realloc을 호출하여 버퍼 크기를 늘려준다는 점이다. 


n에는 최종으로 할당된 메모리 크기가 리턴된다.(만일 getline에서 할당이 없었다면 이전에 입력된 값이 리턴된다.)


결국 getline을 사용하여 이득을 보는 것은 메모리 할당, 재할당, 오버 플로우 체크 등의 곤란한 작업을 하지 않아도 된다는 점이다

그러면 간단한 예제를 살펴보자. 여기서 보이는 예제는 리눅스 man 페이지에 등장하는 예제이다.

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
int  main(void)
{
    FILE *fp;
    char *line = NULL;
    size_t len = 0;
    ssize_t read;
    fp = fopen("/etc/motd", "r");
    if (fp == NULL)  exit(EXIT_FAILURE);

    while ((read = getline(&line, &len, fp)) != -1) {
            printf("Retrieved line of length %zu :\n", read);
            printf("%s", line);
    }
    free(line);  /* getline에서 할당된 힙 메모리 해제 */

    exit(EXIT_SUCCESS);
}


예제는 /etc/motd를 읽어서 한 라인씩 출력한다. 마지막에 사용된 메모리는 free 해주는 것을 잊지 말아야 한다.


2. getdelim

이 함수는 getline과 비슷한데 개행 문자 단위가 아닌 특정 구분자(delimeter)로 읽어들일 필요가 있을 때 사용한다. 따라서 구분자로 사용할 delim 인수에 ASCII 코드값을 넣어주면 된다. 만일 구분자에 New Line 문자를 넣으면 getline과 동일한 기능을 한다.


반응형
Comments