C 언어에서 파일 포인터를 사용해 한 줄씩 읽는 방법

C 언어에서 파일 입출력은 프로그램 개발의 필수 요소 중 하나입니다. 특히, 파일 포인터와 fgets 함수는 파일에서 데이터를 읽고 처리하는 데 있어 강력한 도구로 사용됩니다. 이 기사에서는 파일 포인터와 fgets를 활용해 텍스트 파일을 한 줄씩 읽는 기본 원리부터 실용적인 코드 예제와 응용 방법까지 다루며, 이를 통해 효율적인 파일 처리를 배우는 기회를 제공합니다.

목차

파일 포인터와 파일 열기


파일 포인터는 C 언어에서 파일과 프로그램 간의 데이터 통신을 담당하는 핵심 요소입니다.

파일 포인터란?


파일 포인터는 FILE 타입으로 선언된 변수로, 프로그램이 파일에 접근하거나 데이터를 읽고 쓸 수 있도록 해줍니다. 파일 포인터는 stdio.h 헤더 파일에 정의되어 있습니다.

파일 열기: `fopen` 함수


파일을 열기 위해 fopen 함수를 사용합니다. 이 함수는 파일의 경로와 열기 모드를 매개변수로 받아 파일 포인터를 반환합니다.

FILE *fptr = fopen("example.txt", "r");
if (fptr == NULL) {
    printf("파일을 열 수 없습니다.\n");
    return 1;
}

주요 열기 모드

  • "r": 읽기 전용으로 파일을 엽니다. 파일이 존재해야 합니다.
  • "w": 쓰기 전용으로 파일을 엽니다. 파일이 없으면 새로 생성됩니다.
  • "a": 파일의 끝에 데이터를 추가하기 위해 엽니다.

파일 포인터 유효성 검사


파일 포인터가 NULL인지 확인하여 파일이 성공적으로 열렸는지 확인해야 합니다. 이는 파일이 없거나 권한이 없는 경우 프로그램이 충돌하는 것을 방지합니다.

if (fptr == NULL) {
    perror("파일 열기 오류");
    return 1;
}

파일 포인터와 파일 열기는 파일 작업의 첫 단계로, 이후 데이터를 읽거나 쓰기 위해 반드시 선행되어야 합니다.

`fgets` 함수의 동작 원리

fgets 함수는 C 언어에서 텍스트 파일의 내용을 한 줄씩 읽는 데 사용되는 유용한 함수입니다.

`fgets`의 기본 개념


fgets는 파일에서 문자열을 읽어와 지정된 버퍼에 저장합니다. 이 함수는 파일로부터 한 줄의 텍스트를 읽거나, 버퍼가 가득 찰 때까지 데이터를 가져옵니다.

함수 선언

char *fgets(char *str, int n, FILE *stream);
  • str: 읽어온 데이터를 저장할 버퍼의 포인터
  • n: 버퍼의 크기 (최대 읽어올 문자 수 + 1, 널 종료 포함)
  • stream: 읽을 파일의 파일 포인터

동작 원리

  • 한 번 호출할 때마다 파일의 현재 위치에서 최대 n-1개의 문자를 읽습니다.
  • 읽은 데이터는 str 버퍼에 저장되며, 문자열의 끝에는 널 문자(\0)가 자동으로 추가됩니다.
  • 줄바꿈 문자(\n)가 포함된 경우, fgets는 해당 문자를 그대로 유지합니다.
  • 파일 끝(EOF)에 도달하거나 오류가 발생하면 NULL을 반환합니다.

사용 예제


다음은 fgets를 사용해 파일에서 한 줄씩 읽는 예제입니다.

FILE *fptr = fopen("example.txt", "r");
if (fptr == NULL) {
    perror("파일 열기 실패");
    return 1;
}

char buffer[100];
while (fgets(buffer, sizeof(buffer), fptr) != NULL) {
    printf("%s", buffer); // 한 줄씩 출력
}

fclose(fptr);

장점과 주의사항

  • 장점: 줄 단위로 데이터를 처리할 수 있어 메모리 관리가 효율적입니다.
  • 주의사항: 버퍼 크기(n)를 충분히 설정하지 않으면 데이터가 잘릴 수 있습니다.

fgets는 간단하면서도 강력한 파일 읽기 함수로, 다양한 파일 입출력 작업에서 기본적으로 사용됩니다.

파일 읽기 예제 코드

파일에서 한 줄씩 데이터를 읽는 작업은 C 언어의 기본 파일 입출력 작업 중 하나입니다. 아래는 fgets 함수를 활용한 파일 읽기 예제입니다.

예제 코드: 한 줄씩 읽기

#include <stdio.h>
#include <stdlib.h>

int main() {
    // 파일 열기
    FILE *fptr = fopen("example.txt", "r");
    if (fptr == NULL) {
        perror("파일 열기 실패");
        return 1;
    }

    // 데이터를 읽어올 버퍼 생성
    char buffer[256];

    // 파일에서 한 줄씩 읽기
    while (fgets(buffer, sizeof(buffer), fptr) != NULL) {
        printf("%s", buffer); // 읽은 내용을 출력
    }

    // 파일 닫기
    fclose(fptr);

    return 0;
}

실행 결과


example.txt 파일의 내용이 다음과 같다고 가정합니다:

안녕하세요.
이것은 C 언어 파일 입출력 예제입니다.
즐거운 코딩 되세요!


출력:

안녕하세요.
이것은 C 언어 파일 입출력 예제입니다.
즐거운 코딩 되세요!

코드 설명

  1. 파일 열기
    fopen을 사용하여 읽기 모드로 파일을 엽니다. 실패 시 프로그램을 종료합니다.
  2. 버퍼 생성
    char buffer[256];를 통해 데이터를 읽어올 임시 저장소를 생성합니다.
  3. 반복문으로 파일 읽기
    fgets를 사용해 한 줄씩 읽으며 buffer에 저장된 내용을 출력합니다.
  4. 파일 닫기
    fclose를 호출하여 파일을 닫고 리소스를 해제합니다.

추가: 줄 번호 출력하기


파일의 각 줄에 줄 번호를 출력하려면 코드를 다음과 같이 수정할 수 있습니다.

int line_number = 1;
while (fgets(buffer, sizeof(buffer), fptr) != NULL) {
    printf("%d: %s", line_number++, buffer);
}

이 예제를 통해 파일의 내용을 한 줄씩 읽어오는 방법을 간단히 이해할 수 있습니다.

파일 읽기 중 발생할 수 있는 오류와 처리

파일을 읽는 과정에서 다양한 오류가 발생할 수 있습니다. 이러한 문제를 효과적으로 처리하면 프로그램의 안정성과 신뢰성을 높일 수 있습니다.

주요 오류와 원인

1. 파일 열기 실패


원인: 파일이 존재하지 않거나 권한이 없는 경우 발생합니다.
해결 방법: fopen의 반환값을 확인하고 오류 메시지를 출력합니다.

FILE *fptr = fopen("example.txt", "r");
if (fptr == NULL) {
    perror("파일 열기 실패");
    return 1;
}

2. 버퍼 크기 초과


원인: 읽어올 데이터가 버퍼 크기를 초과하면 데이터가 잘립니다.
해결 방법: 충분히 큰 버퍼를 사용하거나 데이터를 여러 번 나눠 읽습니다.

char buffer[512]; // 필요한 크기를 예측해 설정

3. EOF 처리 누락


원인: 파일 끝(EOF)에 도달했는지 확인하지 않으면 반복문이 무한 루프에 빠질 수 있습니다.
해결 방법: fgets의 반환값을 NULL로 확인하여 EOF를 처리합니다.

while (fgets(buffer, sizeof(buffer), fptr) != NULL) {
    printf("%s", buffer);
}

4. 권한 문제


원인: 파일에 대한 읽기 권한이 없으면 접근이 거부됩니다.
해결 방법: 파일 권한을 확인하고 적절히 변경하거나 관리자 권한으로 실행합니다.

오류 처리 예제


다음은 파일 읽기 중 발생할 수 있는 오류를 처리하는 예제입니다.

#include <stdio.h>
#include <errno.h>
#include <string.h>

int main() {
    FILE *fptr = fopen("example.txt", "r");
    if (fptr == NULL) {
        fprintf(stderr, "오류: %s\n", strerror(errno)); // 오류 메시지 출력
        return 1;
    }

    char buffer[256];
    while (fgets(buffer, sizeof(buffer), fptr) != NULL) {
        printf("%s", buffer);
    }

    if (ferror(fptr)) {
        fprintf(stderr, "파일 읽기 중 오류 발생\n");
    }

    fclose(fptr);
    return 0;
}

ferror 함수


ferror 함수는 파일 작업 중 오류 여부를 확인합니다.

  • 사용법: if (ferror(fptr))
  • 용도: 파일 읽기 또는 쓰기 중 문제가 발생했는지 확인할 때 사용

오류를 미리 방지하는 습관

  • 파일이 존재하는지 확인
  • 충분히 큰 버퍼 사용
  • EOF와 오류 상태를 항상 점검

적절한 오류 처리는 파일 입출력 작업의 신뢰성을 보장하는 중요한 단계입니다.

EOF와 파일 종료 처리

파일 입출력 작업에서는 파일 끝(EOF)과 파일 닫기(fclose)를 올바르게 처리하는 것이 중요합니다. 이를 무시하면 데이터 손실, 메모리 누수, 파일 시스템 오류 등이 발생할 수 있습니다.

EOF란?


EOF(End of File)는 파일의 끝을 나타내는 신호입니다. C 언어에서 파일 읽기 작업은 EOF에 도달하면 중단됩니다.

EOF의 작동 원리

  • fgets, fgetc 같은 함수는 파일 끝에 도달하면 NULL 또는 EOF를 반환합니다.
  • EOF<stdio.h>에 정의된 상수(-1)입니다.

EOF 처리 예제

FILE *fptr = fopen("example.txt", "r");
if (fptr == NULL) {
    perror("파일 열기 실패");
    return 1;
}

char buffer[256];
while (fgets(buffer, sizeof(buffer), fptr) != NULL) {
    printf("%s", buffer); // 파일 내용을 출력
}

// 파일 끝에 도달한 후 확인
if (feof(fptr)) {
    printf("\n파일 끝에 도달했습니다.\n");
}

fclose(fptr);

파일 닫기: `fclose` 함수


파일을 닫는 것은 파일 작업의 마지막 단계로, 사용한 리소스를 해제하고 데이터를 안전하게 저장합니다.

`fclose`의 역할

  • 파일과 연결된 리소스를 시스템에 반환
  • 파일 쓰기 작업이 끝난 후 데이터를 저장
  • 파일 포인터를 유효하지 않은 상태로 변경

`fclose` 사용법

if (fclose(fptr) != 0) {
    perror("파일 닫기 실패");
}

EOF와 `fclose`의 중요성

메모리 누수 방지


파일을 닫지 않으면 프로그램이 종료되더라도 시스템에서 리소스가 해제되지 않을 수 있습니다.

데이터 손실 방지


파일 쓰기 작업 중 fclose를 호출하지 않으면 일부 데이터가 저장되지 않을 수 있습니다.

종합 예제

#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *fptr = fopen("example.txt", "r");
    if (fptr == NULL) {
        perror("파일 열기 실패");
        return 1;
    }

    char buffer[256];
    while (fgets(buffer, sizeof(buffer), fptr) != NULL) {
        printf("%s", buffer);
    }

    if (feof(fptr)) {
        printf("\nEOF에 도달했습니다.\n");
    }

    if (fclose(fptr) == 0) {
        printf("파일이 성공적으로 닫혔습니다.\n");
    } else {
        perror("파일 닫기 실패");
    }

    return 0;
}

EOF와 파일 닫기 처리를 올바르게 수행하면 파일 작업의 안정성과 효율성을 유지할 수 있습니다.

고급 응용: 특정 조건에서 줄 건너뛰기

파일 읽기 작업 중 특정 조건에 따라 줄을 건너뛰어야 할 때가 있습니다. 이를 통해 필요한 데이터만 처리하고 불필요한 데이터를 무시할 수 있습니다.

조건에 따라 줄 건너뛰기


다음 예제는 파일에서 특정 키워드가 포함된 줄만 출력하거나, 특정 키워드가 포함된 줄을 건너뛰는 방법을 보여줍니다.

예제 1: 특정 키워드가 포함된 줄 출력

#include <stdio.h>
#include <string.h>

int main() {
    FILE *fptr = fopen("example.txt", "r");
    if (fptr == NULL) {
        perror("파일 열기 실패");
        return 1;
    }

    char buffer[256];
    const char *keyword = "important";

    while (fgets(buffer, sizeof(buffer), fptr) != NULL) {
        if (strstr(buffer, keyword) != NULL) { // 키워드가 포함된 줄만 출력
            printf("%s", buffer);
        }
    }

    fclose(fptr);
    return 0;
}

예제 2: 특정 키워드가 포함된 줄 건너뛰기

#include <stdio.h>
#include <string.h>

int main() {
    FILE *fptr = fopen("example.txt", "r");
    if (fptr == NULL) {
        perror("파일 열기 실패");
        return 1;
    }

    char buffer[256];
    const char *keyword = "skip";

    while (fgets(buffer, sizeof(buffer), fptr) != NULL) {
        if (strstr(buffer, keyword) != NULL) { // 키워드가 포함된 줄은 건너뛰기
            continue;
        }
        printf("%s", buffer);
    }

    fclose(fptr);
    return 0;
}

조건 조합을 활용한 고급 필터링


파일의 줄을 여러 조건으로 필터링할 수도 있습니다. 예를 들어, 특정 키워드가 포함되면서 길이가 50자 이상인 줄만 출력하는 코드입니다.

#include <stdio.h>
#include <string.h>

int main() {
    FILE *fptr = fopen("example.txt", "r");
    if (fptr == NULL) {
        perror("파일 열기 실패");
        return 1;
    }

    char buffer[256];
    const char *keyword = "filter";

    while (fgets(buffer, sizeof(buffer), fptr) != NULL) {
        if (strstr(buffer, keyword) != NULL && strlen(buffer) >= 50) {
            printf("%s", buffer);
        }
    }

    fclose(fptr);
    return 0;
}

응용 예시

  • 로그 파일 분석: 오류 로그를 포함한 줄만 출력
  • 데이터 파일 처리: 특정 값에 해당하는 데이터만 추출
  • 설정 파일 필터링: 주석이나 빈 줄을 건너뛰고 유효한 설정만 처리

이처럼 조건에 따라 줄을 건너뛰거나 필터링하는 기법은 다양한 데이터 처리 작업에서 유용하게 사용됩니다.

요약

본 기사에서는 C 언어에서 파일 포인터와 fgets 함수를 활용해 파일을 한 줄씩 읽는 방법을 다루었습니다. 파일 포인터를 사용해 파일을 열고, fgets로 데이터를 읽는 기본 과정부터 조건에 따라 특정 줄을 건너뛰는 고급 응용까지 설명했습니다.

이와 함께 파일 작업 중 발생할 수 있는 오류 처리, EOF 처리의 중요성, 그리고 안전한 파일 종료 방법을 소개했습니다. 이러한 기술을 통해 파일 입출력 작업을 더욱 효율적이고 안정적으로 수행할 수 있습니다.

이제 C 언어에서 파일 읽기에 자신감을 가지고 다양한 응용 프로그램에 활용할 수 있을 것입니다.

목차