C언어에서 파일 입출력: stdio.h 함수 완벽 가이드

C언어에서 파일 입출력은 데이터 저장 및 관리를 위한 핵심적인 기능입니다. stdio.h 라이브러리를 활용하면 파일을 열고, 데이터를 읽거나 쓰며, 파일을 닫는 과정을 간단히 구현할 수 있습니다. 이 기사는 파일 입출력의 기본 개념과 주요 함수를 체계적으로 설명하여, 프로그래밍 초보자도 쉽게 이해할 수 있도록 돕습니다.

파일 입출력의 기본 개념


파일 입출력은 외부 저장소에 데이터를 읽거나 쓰는 과정을 의미합니다. C언어에서는 파일을 다루기 위해 FILE 포인터와 관련 함수를 사용합니다.

파일 입출력의 주요 단계

  1. 파일 열기: 파일을 열기 위해 fopen 함수를 사용하며, 파일을 읽기, 쓰기, 추가 등 다양한 모드로 열 수 있습니다.
  2. 데이터 읽기/쓰기: 파일의 데이터를 읽거나 새 데이터를 파일에 기록합니다. 이를 위해 fgetc, fgets, fread 또는 fputc, fputs, fwrite 함수를 사용합니다.
  3. 파일 닫기: 파일 작업을 마친 후 fclose 함수를 호출하여 시스템 자원을 해제합니다.

파일 모드


fopen 함수는 파일을 열 때 다음과 같은 모드를 사용합니다:

  • "r": 읽기 전용 모드. 파일이 존재해야 함.
  • "w": 쓰기 전용 모드. 파일이 없으면 생성하고, 있으면 내용을 덮어씀.
  • "a": 추가 모드. 파일이 없으면 생성하며, 있으면 기존 내용 뒤에 덧붙임.

파일 입출력은 데이터 저장과 활용에 필수적인 도구이며, 올바른 파일 관리를 통해 안정적인 프로그램 작성을 가능하게 합니다.

`fopen`과 `fclose` 함수

`fopen` 함수


fopen 함수는 파일을 열고 작업을 시작하기 위한 첫 단계입니다. 이 함수는 파일을 열고 FILE 포인터를 반환합니다.

사용법

FILE *fopen(const char *filename, const char *mode);
  • filename: 열고자 하는 파일의 이름(경로 포함).
  • mode: 파일을 열 때 사용할 모드(읽기, 쓰기 등).

예시

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

`fclose` 함수


fclose 함수는 파일 작업을 완료한 후 시스템 자원을 해제하는 데 사용됩니다. 파일을 열었으면 반드시 닫아야 합니다.

사용법

int fclose(FILE *stream);
  • stream: 닫을 파일의 FILE 포인터.
  • 반환값: 성공 시 0, 실패 시 EOF를 반환.

예시

if (fclose(file) != 0) {
    printf("파일을 닫는 데 실패했습니다.\n");
}

중요 사항

  1. 파일 작업이 끝나면 반드시 fclose를 호출하여 자원을 해제해야 메모리 누수를 방지할 수 있습니다.
  2. fopen 호출 후 반환값이 NULL인지 확인하여 파일 열기 실패를 처리해야 합니다.

이 두 함수를 올바르게 사용하는 것은 안전하고 효율적인 파일 입출력의 기초입니다.

파일 읽기 함수: `fgetc`, `fgets`, `fread`

`fgetc` 함수


fgetc는 파일에서 한 문자씩 읽어옵니다. 주로 반복문과 함께 사용하여 파일의 모든 문자를 처리할 때 유용합니다.

사용법

int fgetc(FILE *stream);
  • stream: 읽을 파일의 FILE 포인터.
  • 반환값: 읽은 문자(ASCII 코드). 파일의 끝에 도달하면 EOF를 반환.

예시

FILE *file = fopen("example.txt", "r");
if (file != NULL) {
    char ch;
    while ((ch = fgetc(file)) != EOF) {
        putchar(ch);
    }
    fclose(file);
}

`fgets` 함수


fgets는 파일에서 한 줄씩 읽어오는 데 사용됩니다. 줄 단위 처리가 필요한 경우 적합합니다.

사용법

char *fgets(char *str, int n, FILE *stream);
  • str: 읽은 데이터를 저장할 버퍼.
  • n: 읽을 최대 문자 수(종료 문자 포함).
  • stream: 읽을 파일의 FILE 포인터.

예시

FILE *file = fopen("example.txt", "r");
if (file != NULL) {
    char line[100];
    while (fgets(line, sizeof(line), file) != NULL) {
        printf("%s", line);
    }
    fclose(file);
}

`fread` 함수


fread는 바이너리 데이터를 읽는 데 사용됩니다. 구조체나 큰 데이터 블록을 처리할 때 유용합니다.

사용법

size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
  • ptr: 읽은 데이터를 저장할 버퍼.
  • size: 각 데이터 블록의 크기(바이트 단위).
  • count: 읽을 데이터 블록의 개수.
  • stream: 읽을 파일의 FILE 포인터.

예시

FILE *file = fopen("data.bin", "rb");
if (file != NULL) {
    int buffer[10];
    size_t itemsRead = fread(buffer, sizeof(int), 10, file);
    printf("읽은 항목 수: %zu\n", itemsRead);
    fclose(file);
}

차이점 요약

  • fgetc: 파일에서 한 문자씩 읽음.
  • fgets: 파일에서 한 줄씩 읽음.
  • fread: 바이너리 데이터를 블록 단위로 읽음.

적절한 파일 읽기 함수를 선택하면 효율적인 파일 처리가 가능합니다.

파일 쓰기 함수: `fputc`, `fputs`, `fwrite`

`fputc` 함수


fputc는 파일에 한 문자를 씁니다. 문자 단위로 파일에 데이터를 기록할 때 유용합니다.

사용법

int fputc(int ch, FILE *stream);
  • ch: 파일에 쓸 문자(ASCII 코드).
  • stream: 데이터를 기록할 파일의 FILE 포인터.
  • 반환값: 기록한 문자의 ASCII 코드, 실패 시 EOF 반환.

예시

FILE *file = fopen("example.txt", "w");
if (file != NULL) {
    fputc('A', file);
    fputc('B', file);
    fclose(file);
}

`fputs` 함수


fputs는 문자열 단위로 데이터를 기록합니다. 줄이나 문장을 파일에 쓸 때 적합합니다.

사용법

int fputs(const char *str, FILE *stream);
  • str: 파일에 쓸 문자열.
  • stream: 데이터를 기록할 파일의 FILE 포인터.
  • 반환값: 성공 시 0 이상, 실패 시 EOF 반환.

예시

FILE *file = fopen("example.txt", "w");
if (file != NULL) {
    fputs("Hello, World!\n", file);
    fputs("C programming is fun.", file);
    fclose(file);
}

`fwrite` 함수


fwrite는 바이너리 데이터를 블록 단위로 기록합니다. 구조체 또는 대량 데이터를 효율적으로 저장할 때 사용됩니다.

사용법

size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
  • ptr: 기록할 데이터가 저장된 버퍼.
  • size: 각 데이터 블록의 크기(바이트 단위).
  • count: 기록할 데이터 블록의 개수.
  • stream: 데이터를 기록할 파일의 FILE 포인터.
  • 반환값: 성공적으로 기록한 블록의 개수.

예시

FILE *file = fopen("data.bin", "wb");
if (file != NULL) {
    int buffer[] = {1, 2, 3, 4, 5};
    size_t itemsWritten = fwrite(buffer, sizeof(int), 5, file);
    printf("기록한 항목 수: %zu\n", itemsWritten);
    fclose(file);
}

차이점 요약

  • fputc: 한 문자를 파일에 기록.
  • fputs: 문자열을 파일에 기록.
  • fwrite: 바이너리 데이터를 블록 단위로 기록.

적절한 파일 쓰기 함수를 사용하면 효율적으로 데이터를 저장할 수 있습니다.

파일 포인터와 `ftell`, `fseek`, `rewind`

파일 포인터


파일 포인터는 파일 내 현재 읽기 또는 쓰기 위치를 가리키는 값입니다. 파일 작업 중에 특정 위치로 이동하거나 현재 위치를 확인할 때 유용합니다.

`ftell` 함수


ftell은 현재 파일 포인터의 위치를 반환합니다. 파일 시작점으로부터 몇 바이트 떨어져 있는지 확인할 수 있습니다.

사용법

long ftell(FILE *stream);
  • stream: 파일의 FILE 포인터.
  • 반환값: 파일의 시작점부터 현재 위치까지의 바이트 수, 실패 시 -1 반환.

예시

FILE *file = fopen("example.txt", "r");
if (file != NULL) {
    fgetc(file); // 파일에서 한 문자 읽기
    long position = ftell(file);
    printf("현재 위치: %ld 바이트\n", position);
    fclose(file);
}

`fseek` 함수


fseek은 파일 포인터를 원하는 위치로 이동시킵니다. 파일 내에서 데이터를 무작위로 접근할 때 유용합니다.

사용법

int fseek(FILE *stream, long offset, int whence);
  • stream: 파일의 FILE 포인터.
  • offset: 이동할 바이트 수.
  • whence: 기준점(아래 참고).
  • SEEK_SET: 파일의 시작점 기준.
  • SEEK_CUR: 현재 위치 기준.
  • SEEK_END: 파일의 끝 기준.
  • 반환값: 성공 시 0, 실패 시 -1.

예시

FILE *file = fopen("example.txt", "r");
if (file != NULL) {
    fseek(file, 10, SEEK_SET); // 시작점으로부터 10바이트 이동
    printf("새 위치: %ld 바이트\n", ftell(file));
    fclose(file);
}

`rewind` 함수


rewind는 파일 포인터를 파일의 시작점으로 즉시 이동시킵니다.

사용법

void rewind(FILE *stream);
  • stream: 파일의 FILE 포인터.

예시

FILE *file = fopen("example.txt", "r");
if (file != NULL) {
    fgetc(file); // 파일에서 한 문자 읽기
    rewind(file); // 시작점으로 이동
    printf("위치: %ld 바이트\n", ftell(file)); // 항상 0 출력
    fclose(file);
}

차이점 요약

  • ftell: 현재 파일 포인터 위치를 반환.
  • fseek: 파일 포인터를 지정된 위치로 이동.
  • rewind: 파일 포인터를 시작점으로 이동.

이 함수들은 파일 내 특정 위치로 효율적으로 이동하거나 데이터를 무작위로 접근하는 데 필수적입니다.

오류 처리와 `perror`, `feof` 함수

`perror` 함수


perror는 파일 작업 중 발생한 오류를 간단히 출력합니다. 오류 원인을 명확히 파악할 때 유용합니다.

사용법

void perror(const char *message);
  • message: 출력 메시지로, 오류 메시지와 함께 표시됨.

예시

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

출력 예시

파일 열기 오류: No such file or directory

`feof` 함수


feof는 파일 끝(EOF)에 도달했는지 확인하는 데 사용됩니다. 반복문에서 파일 데이터를 읽는 도중 파일 끝을 정확히 확인할 수 있습니다.

사용법

int feof(FILE *stream);
  • stream: 파일의 FILE 포인터.
  • 반환값: EOF에 도달하면 0이 아닌 값을 반환, 그렇지 않으면 0을 반환.

예시

FILE *file = fopen("example.txt", "r");
if (file != NULL) {
    char ch;
    while (!feof(file)) {
        ch = fgetc(file);
        if (ch != EOF) {
            putchar(ch);
        }
    }
    fclose(file);
}

추가적인 오류 처리


파일 작업 중 발생할 수 있는 일반적인 오류는 다음과 같습니다:

  • 파일 없음: fopen에서 반환된 포인터가 NULL인지 확인.
  • 읽기/쓰기 실패: 반환값을 확인하여 적절히 처리.
  • 잘못된 파일 포인터 사용: 닫힌 파일에 접근하지 않도록 주의.

예시 코드: 파일 오류 확인

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

if (fgetc(file) == EOF && feof(file)) {
    printf("파일이 비어 있습니다.\n");
}

fclose(file);

정리

  • perror: 오류 메시지를 명확히 출력.
  • feof: 파일 끝에 도달했는지 확인.

효율적인 오류 처리는 파일 입출력의 안정성을 높이는 중요한 요소입니다.

응용 예시: 텍스트 파일 읽기 및 쓰기

예시 코드: 텍스트 파일 쓰기


아래 코드는 텍스트 파일에 데이터를 기록하는 간단한 예시입니다.

#include <stdio.h>

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

    fputs("C 언어 파일 입출력 예제\n", file);
    fputs("이 내용은 파일에 저장됩니다.\n", file);

    fclose(file);
    printf("파일 쓰기 완료\n");
    return 0;
}

결과: output.txt 파일이 생성되며 다음 내용이 저장됩니다.

C 언어 파일 입출력 예제
이 내용은 파일에 저장됩니다.

예시 코드: 텍스트 파일 읽기


다음 코드는 텍스트 파일의 내용을 읽어 출력합니다.

#include <stdio.h>

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

    char line[100];
    while (fgets(line, sizeof(line), file) != NULL) {
        printf("%s", line);
    }

    fclose(file);
    return 0;
}

결과: output.txt 파일의 내용이 터미널에 출력됩니다.

예시 코드: 파일 복사


다음 코드는 텍스트 파일을 읽어 다른 파일에 복사합니다.

#include <stdio.h>

int main() {
    FILE *src = fopen("output.txt", "r");
    FILE *dest = fopen("copy.txt", "w");

    if (src == NULL || dest == NULL) {
        perror("파일 열기 실패");
        return 1;
    }

    char ch;
    while ((ch = fgetc(src)) != EOF) {
        fputc(ch, dest);
    }

    fclose(src);
    fclose(dest);
    printf("파일 복사 완료\n");
    return 0;
}

결과: copy.txt 파일이 생성되며 output.txt의 내용이 복사됩니다.

요약


이 예제들을 통해 다음을 배울 수 있습니다:

  1. 파일 쓰기를 위한 fputs 사용.
  2. 파일 읽기를 위한 fgets 사용.
  3. 파일 데이터를 읽어 다른 파일에 저장하기.

실습을 통해 파일 입출력을 효과적으로 이해하고 응용할 수 있습니다.