C 언어에서 파일 읽기 및 쓰기의 기본 방법과 예제

C 언어에서 파일 읽기와 쓰기는 데이터를 저장하고 관리하는 데 있어 중요한 역할을 합니다. 이를 통해 프로그램은 실행 중 생성된 데이터를 영구적으로 보존하거나, 외부 데이터를 활용해 다양한 작업을 수행할 수 있습니다. 본 기사에서는 C 언어의 파일 입출력 기본 개념과 구현 방법, 그리고 실용적인 예제들을 통해 파일 처리를 명확히 이해할 수 있도록 안내합니다.

목차

파일 입출력의 필요성


파일 입출력은 데이터를 영구적으로 저장하고 다른 프로그램 또는 사용자와 정보를 교환하는 데 필수적인 기능입니다.

주요 용도

  • 데이터 저장: 프로그램 종료 후에도 데이터를 보존하기 위해 필요합니다.
  • 데이터 읽기: 외부에서 제공된 파일 데이터를 분석하거나 처리할 수 있습니다.
  • 로그 기록: 시스템 상태나 오류를 파일에 기록해 추후 분석할 수 있습니다.

활용 사례

  • 사용자가 작성한 데이터를 텍스트 파일에 저장하는 메모장 프로그램.
  • 금융 데이터를 읽고 계산하는 스프레드시트 애플리케이션.
  • 게임 내 진행 상황을 저장하고 복구하는 저장 시스템.

파일 입출력은 프로그램의 유연성과 기능성을 확장하는 중요한 요소입니다.

C 언어에서 파일을 여는 방법

파일을 열기 위해 C 언어는 표준 라이브러리 함수 fopen을 제공합니다. 이 함수는 파일을 열고, 해당 파일에 대한 작업을 수행할 수 있도록 파일 포인터를 반환합니다.

fopen 함수의 기본 사용법


fopen 함수는 두 개의 매개변수를 받습니다:

  1. 파일 이름: 파일 경로를 포함한 이름을 문자열로 지정합니다.
  2. 모드: 파일을 열 때의 동작을 정의하는 문자열입니다.
FILE *fopen(const char *filename, const char *mode);

파일 모드의 종류

  • “r”: 읽기 전용. 파일이 존재해야 합니다.
  • “w”: 쓰기 전용. 파일이 없으면 생성되며, 기존 파일은 내용을 덮어씁니다.
  • “a”: 추가 전용. 파일이 없으면 생성되며, 기존 파일에 내용을 추가합니다.
  • “r+”: 읽기/쓰기. 파일이 존재해야 합니다.
  • “w+”: 읽기/쓰기. 파일이 없으면 생성되며, 기존 파일은 내용을 덮어씁니다.
  • “a+”: 읽기/쓰기. 파일이 없으면 생성되며, 기존 파일에 내용을 추가합니다.

예제 코드

#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "r");
    if (file == NULL) {
        printf("파일을 열 수 없습니다.\n");
        return 1;
    }
    printf("파일을 성공적으로 열었습니다.\n");
    fclose(file);
    return 0;
}

유의사항

  • 파일 경로: 파일이 실행 파일과 같은 디렉토리에 없는 경우, 절대 경로를 사용하거나 올바른 상대 경로를 지정해야 합니다.
  • 에러 처리: fopen 함수는 파일을 열 수 없을 경우 NULL을 반환하므로, 항상 반환 값을 확인하는 것이 중요합니다.

파일을 성공적으로 열면, 이후의 작업(읽기, 쓰기 등)을 위한 기반이 마련됩니다.

파일에 쓰기

C 언어에서는 파일에 데이터를 쓰기 위해 fprintf, fputs 등의 표준 라이브러리 함수를 사용합니다. 이러한 함수들은 파일 포인터를 통해 파일에 접근하고, 원하는 데이터를 기록할 수 있도록 합니다.

fprintf 함수


fprintf 함수는 서식 지정 문자열을 사용하여 파일에 데이터를 기록할 때 사용됩니다.

int fprintf(FILE *stream, const char *format, ...);

사용 예제:

#include <stdio.h>

int main() {
    FILE *file = fopen("output.txt", "w");
    if (file == NULL) {
        printf("파일을 열 수 없습니다.\n");
        return 1;
    }

    fprintf(file, "이것은 %d번째 예제입니다.\n", 1);
    fprintf(file, "파일 쓰기가 성공적으로 완료되었습니다.\n");

    fclose(file);
    return 0;
}

fputs 함수


fputs 함수는 문자열을 파일에 기록할 때 사용됩니다.

int fputs(const char *str, FILE *stream);

사용 예제:

#include <stdio.h>

int main() {
    FILE *file = fopen("output.txt", "w");
    if (file == NULL) {
        printf("파일을 열 수 없습니다.\n");
        return 1;
    }

    fputs("이것은 간단한 문자열입니다.\n", file);
    fputs("파일에 성공적으로 기록되었습니다.\n", file);

    fclose(file);
    return 0;
}

차이점

  • fprintf: 문자열뿐만 아니라 변수, 숫자 등을 서식화하여 기록할 수 있습니다.
  • fputs: 단순히 문자열을 기록할 때 사용됩니다.

파일 쓰기 주의점

  1. 쓰기 모드 확인: 파일이 쓰기 가능 모드(“w”, “a”, “w+” 등)로 열려 있어야 합니다.
  2. 에러 처리: 파일 포인터가 NULL인지 확인하고, 쓰기 작업 중 오류가 발생했는지 확인해야 합니다.
  3. 리소스 관리: 작업 후 반드시 fclose로 파일을 닫아야 리소스 누수가 발생하지 않습니다.

파일 쓰기는 데이터를 파일에 저장하고 외부에서 재사용할 수 있도록 하는 중요한 작업입니다. 적절한 함수와 모드를 사용해 안전하게 파일에 데이터를 기록하세요.

파일에서 읽기

C 언어에서는 파일에서 데이터를 읽기 위해 fscanf, fgets 등의 표준 라이브러리 함수를 사용합니다. 이러한 함수들은 파일 포인터를 통해 파일 내용을 프로그램으로 불러올 수 있게 합니다.

fscanf 함수


fscanf 함수는 서식 지정 문자열을 사용하여 파일에서 데이터를 읽습니다.

int fscanf(FILE *stream, const char *format, ...);

사용 예제:

#include <stdio.h>

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

    int number;
    char name[50];
    fscanf(file, "%d %s", &number, name);
    printf("읽은 데이터: %d, %s\n", number, name);

    fclose(file);
    return 0;
}

fgets 함수


fgets 함수는 파일에서 한 줄씩 문자열을 읽는 데 사용됩니다.

char *fgets(char *str, int n, FILE *stream);

사용 예제:

#include <stdio.h>

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

    char buffer[100];
    while (fgets(buffer, sizeof(buffer), file) != NULL) {
        printf("읽은 줄: %s", buffer);
    }

    fclose(file);
    return 0;
}

차이점

  • fscanf: 특정 서식에 따라 데이터를 읽어올 때 유용합니다.
  • fgets: 파일에서 한 줄씩 읽을 때 적합하며, 문자열을 다룰 때 안전합니다.

파일 읽기 주의점

  1. 파일 모드 확인: 파일이 읽기 가능 모드(“r”, “r+” 등)로 열려 있어야 합니다.
  2. EOF(파일 끝) 확인: 읽기 작업 중 EOF를 만나면 파일 읽기가 종료됩니다.
  3. 버퍼 크기: fgets를 사용할 때 버퍼 크기를 파일 데이터 크기보다 크게 설정하여 데이터 손실을 방지합니다.

EOF 처리 예제

#include <stdio.h>

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

    char ch;
    while ((ch = fgetc(file)) != EOF) {
        putchar(ch);  // 읽은 문자 출력
    }

    fclose(file);
    return 0;
}

파일 읽기는 외부 데이터를 프로그램으로 가져와 다양한 작업을 수행하는 데 필수적입니다. 적절한 함수와 에러 처리를 통해 안전하게 파일을 읽어보세요.

파일 닫기

C 언어에서 파일 작업이 끝난 후에는 fclose 함수를 사용해 파일을 닫아야 합니다. 이를 통해 파일 관련 리소스를 해제하고 데이터 손실이나 메모리 누수를 방지할 수 있습니다.

fclose 함수 사용법


fclose 함수는 열려 있는 파일 포인터를 닫습니다.

int fclose(FILE *stream);
  • 반환값:
  • 0: 파일이 성공적으로 닫힘.
  • EOF(-1): 파일 닫기 실패.

기본 예제

#include <stdio.h>

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

    // 파일 작업 수행
    printf("파일 작업을 수행합니다.\n");

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

    return 0;
}

왜 파일을 닫아야 하는가?

  1. 리소스 관리: 파일이 열려 있는 동안 시스템 리소스(메모리, 파일 핸들)가 사용됩니다. 이를 해제하지 않으면 리소스 누수가 발생할 수 있습니다.
  2. 데이터 손실 방지: 쓰기 작업 후 파일을 닫지 않으면, 작성된 내용이 실제 파일에 기록되지 않을 수 있습니다.
  3. 파일 잠금 해제: 파일이 열려 있는 동안 다른 프로세스에서 접근할 수 없을 수 있습니다. 파일을 닫아야 다른 프로그램이나 사용자가 파일에 접근할 수 있습니다.

에러 처리


파일 닫기 실패는 드물지만, 파일 시스템 오류나 잘못된 파일 포인터를 사용할 경우 발생할 수 있습니다. 반환값을 확인해 안전하게 처리하세요.

에러 처리 예제:

if (fclose(file) != 0) {
    perror("파일 닫기 오류");
}

fclose를 호출하지 않으면?

  • 시스템 리소스가 계속 사용 상태로 남아 있을 수 있습니다.
  • 쓰기 작업이 완료되지 않을 수 있습니다.
  • 프로그램 종료 시 파일 시스템 문제가 발생할 가능성이 있습니다.

파일 닫기는 간단하지만 중요한 작업입니다. 항상 파일 작업이 완료되면 fclose를 호출하여 리소스를 정리하는 습관을 가지세요.

에러 처리 및 예외 상황

파일 입출력 과정에서 발생할 수 있는 다양한 에러를 처리하는 것은 안정적인 프로그램 개발의 핵심입니다. C 언어에서는 파일 작업 중 에러를 확인하고 처리하기 위한 여러 방법을 제공합니다.

에러 처리 기본 원칙

  1. 반환값 확인: 파일 함수의 반환값을 항상 확인하여 작업 성공 여부를 판단합니다.
  2. 파일 포인터 확인: 파일을 열 때 반환된 파일 포인터가 NULL인지 검사합니다.
  3. perror 함수 사용: 에러 발생 시 시스템이 제공하는 에러 메시지를 출력합니다.

주요 에러 및 처리 방법

파일 열기 실패


파일이 없거나 권한이 없을 경우 fopen이 NULL을 반환합니다.

FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
    perror("파일 열기 오류");
    return 1;
}

파일 읽기/쓰기 에러

  • fscanf, fprintf, fgets 등의 함수는 작업 실패 시 비정상적인 값을 반환합니다.
  • ferror 함수로 파일 스트림의 에러 상태를 확인할 수 있습니다.
#include <stdio.h>

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

    char buffer[100];
    if (fgets(buffer, sizeof(buffer), file) == NULL) {
        if (ferror(file)) {
            perror("파일 읽기 오류");
        }
    }

    fclose(file);
    return 0;
}

파일 끝 (EOF) 확인


파일을 읽는 동안 파일 끝을 만나면 EOF가 반환됩니다. feof 함수를 사용해 EOF 상태를 확인할 수 있습니다.

while (!feof(file)) {
    char ch = fgetc(file);
    if (ferror(file)) {
        perror("파일 읽기 중 오류");
        break;
    }
    if (ch != EOF) putchar(ch);
}

일반적인 예외 상황

  1. 파일이 존재하지 않을 때
    파일을 열기 전에 존재 여부를 확인하거나, 쓰기 모드로 파일을 생성합니다.
  2. 파일 접근 권한 부족
    파일 권한을 변경하거나, 관리자 권한으로 실행합니다.
  3. 디스크 공간 부족
    쓰기 작업 시 디스크 공간을 확인합니다.

에러 처리 모범 사례

  • 모든 파일 작업 후 반환값을 체크합니다.
  • 에러 메시지를 사용자에게 명확히 전달하여 문제 해결을 돕습니다.
  • 파일 닫기 전에 에러 상태를 항상 확인합니다.
if (fclose(file) != 0) {
    perror("파일 닫기 오류");
}

에러 처리를 통해 프로그램의 안정성과 신뢰성을 높이고, 예외 상황에서도 적절히 대응할 수 있습니다.

요약

C 언어에서 파일 읽기와 쓰기는 데이터를 저장하고 관리하는 데 필수적인 기능입니다. 이번 기사에서는 파일을 열고(fopen), 데이터를 쓰고(fprintf, fputs), 읽고(fscanf, fgets), 닫는(fclose) 방법과 에러 처리 방법을 다뤘습니다.

파일 입출력은 프로그램의 기능을 확장하고, 데이터의 영구 보존 및 처리를 가능하게 합니다. 적절한 함수 사용과 에러 처리를 통해 안정적이고 효율적인 파일 관리를 구현할 수 있습니다.

목차