C언어에서 포맷된 파일 입출력 활용법 (fprintf, fscanf)

C언어에서 파일 입출력은 프로그램에서 데이터를 저장하거나 읽어오는 필수적인 기능입니다. 이 기사에서는 포맷된 파일 입출력 함수인 fprintffscanf에 대해 자세히 설명합니다.
파일 입출력 함수는 파일을 다룰 때 사용하는 기본적인 도구입니다. C언어에서는 여러 종류의 파일 입출력 함수들이 존재하지만, fprintffscanf는 데이터를 포맷에 맞춰 처리할 수 있어 매우 유용합니다. 이 함수들은 각각 파일에 데이터를 기록하거나 파일로부터 데이터를 읽을 때 주로 사용됩니다.
fprintf 함수는 파일에 데이터를 포맷에 맞춰 출력하는 함수입니다. 이 함수는 콘솔에 출력을 하는 printf 함수와 유사하지만, 출력 대상이 화면이 아니라 파일이라는 차이점이 있습니다. fprintf는 첫 번째 인자로 파일 포인터를 받아 해당 파일에 데이터를 기록합니다. 이를 통해 텍스트 파일에 데이터를 형식화하여 저장할 수 있습니다.

예시 코드:

#include <stdio.h>

int main() {
    FILE *file = fopen("output.txt", "w");
    if (file != NULL) {
        fprintf(file, "Hello, %s! Your score is %d.\n", "Alice", 90);
        fclose(file);
    } else {
        printf("파일을 열 수 없습니다.\n");
    }
    return 0;
}

위 코드에서는 output.txt 파일에 “Hello, Alice! Your score is 90.”라는 포맷된 문자열을 기록합니다.
fprintf 함수의 기본 구문은 다음과 같습니다:

int fprintf(FILE *stream, const char *format, ...);
  • stream: 데이터를 출력할 파일의 포인터입니다. fopen 함수를 사용하여 파일을 열고 그 포인터를 전달합니다.
  • format: 출력할 데이터의 포맷을 지정하는 문자열입니다. %d, %s, %f 등의 포맷 지정자를 사용하여 출력할 데이터를 형식에 맞게 지정합니다.
  • : 포맷 문자열에 맞는 값들이 순차적으로 전달됩니다.

예제 1: 정수와 문자열을 파일에 출력하기

#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "w");
    if (file != NULL) {
        int age = 25;
        const char *name = "John";
        fprintf(file, "Name: %s, Age: %d\n", name, age);
        fclose(file);
    } else {
        printf("파일을 열 수 없습니다.\n");
    }
    return 0;
}

위 코드에서는 example.txt 파일에 Name: John, Age: 25와 같은 형식으로 데이터를 기록합니다.

예제 2: 실수와 문자열을 출력하기

#include <stdio.h>

int main() {
    FILE *file = fopen("data.txt", "w");
    if (file != NULL) {
        float pi = 3.14159;
        fprintf(file, "Value of Pi: %.2f\n", pi);
        fclose(file);
    } else {
        printf("파일을 열 수 없습니다.\n");
    }
    return 0;
}

위 코드에서는 data.txt 파일에 Value of Pi: 3.14와 같은 형식으로 실수 값을 출력합니다. %f는 실수를 출력하는 포맷 지정자이며, %.2f는 소수점 두 자릿수까지 출력하도록 합니다.
fscanf는 파일에서 데이터를 읽어들이는 함수로, scanf와 유사하게 사용됩니다. fscanf는 파일로부터 데이터를 읽고, 지정된 포맷에 맞춰 변수에 저장합니다. 이 함수는 첫 번째 인자로 파일 포인터를 받으며, 그 후 포맷 문자열과 해당 포맷에 맞는 변수들을 전달합니다.

fscanf의 기본 구문은 다음과 같습니다:

int fscanf(FILE *stream, const char *format, ...);
  • stream: 데이터를 읽을 파일의 포인터입니다. fopen 함수로 열린 파일 포인터를 전달합니다.
  • format: 읽을 데이터의 형식을 지정하는 포맷 문자열입니다. %d, %s, %f 등의 포맷 지정자를 사용하여 파일에서 데이터를 읽을 때 어떤 형식으로 읽을지 정의합니다.
  • : 포맷에 맞는 변수들이 전달됩니다. 읽은 데이터는 해당 변수에 저장됩니다.

예시 코드: 파일에서 데이터 읽기

#include <stdio.h>

int main() {
    FILE *file = fopen("input.txt", "r");
    if (file != NULL) {
        int age;
        char name[50];
        fscanf(file, "Name: %s, Age: %d", name, &age);
        printf("Name: %s, Age: %d\n", name, age);
        fclose(file);
    } else {
        printf("파일을 열 수 없습니다.\n");
    }
    return 0;
}

위 코드에서는 input.txt 파일에서 “Name: John, Age: 25″와 같은 형식의 데이터를 읽어, name 변수와 age 변수에 값을 저장합니다. fscanf 함수는 해당 포맷에 맞는 데이터를 읽고, 그 값을 각각 변수에 할당합니다.
fscanf 함수는 파일에서 데이터를 읽을 때 유용하지만, 올바른 포맷을 사용하는 것이 중요합니다. 데이터를 읽을 때 포맷 지정자는 매우 중요하며, 이를 통해 변수에 정확하게 값을 저장할 수 있습니다. 여러 가지 포맷 지정자를 사용하여 다양한 데이터 유형을 처리할 수 있습니다.

예제 1: 정수와 문자열을 파일에서 읽기

#include <stdio.h>

int main() {
    FILE *file = fopen("data.txt", "r");
    if (file != NULL) {
        int age;
        char name[50];
        fscanf(file, "Name: %s, Age: %d", name, &age);
        printf("Name: %s, Age: %d\n", name, age);
        fclose(file);
    } else {
        printf("파일을 열 수 없습니다.\n");
    }
    return 0;
}

위 코드에서는 data.txt 파일에서 “Name: John, Age: 25″와 같은 형식의 데이터를 읽습니다. fscanf%s를 사용하여 문자열을 읽고, %d를 사용하여 정수 값을 읽어들입니다.

예제 2: 실수와 문자열을 읽기

#include <stdio.h>

int main() {
    FILE *file = fopen("data.txt", "r");
    if (file != NULL) {
        float pi;
        fscanf(file, "Pi: %f", &pi);
        printf("Value of Pi: %.2f\n", pi);
        fclose(file);
    } else {
        printf("파일을 열 수 없습니다.\n");
    }
    return 0;
}

이 코드는 파일에서 Pi: 3.14159와 같은 실수 값을 읽어 변수에 저장합니다. fscanf%f 포맷 지정자를 사용하여 실수 값을 읽어들입니다.

주의 사항: fscanf의 포맷 지정자와 변수 타입 일치

fscanf에서 지정한 포맷과 읽을 변수의 타입이 일치해야 합니다. 예를 들어, %d로 읽은 값을 int형 변수에 저장하고, %f로 읽은 값을 float형 변수에 저장해야 합니다. 형식이 맞지 않으면 예기치 않은 동작이나 오류가 발생할 수 있습니다.

예제 3: 여러 데이터 읽기

#include <stdio.h>

int main() {
    FILE *file = fopen("data.txt", "r");
    if (file != NULL) {
        int day, month, year;
        fscanf(file, "%d/%d/%d", &day, &month, &year);
        printf("Date: %02d/%02d/%04d\n", day, month, year);
        fclose(file);
    } else {
        printf("파일을 열 수 없습니다.\n");
    }
    return 0;
}

위 예제는 “30/12/2023″와 같은 날짜 형식 데이터를 읽어서, 각 요소를 day, month, year 변수에 저장하는 코드입니다. %d/%d/%d 포맷을 사용하여 각각의 값을 읽고 출력합니다.
fprintffscanf에서 사용하는 포맷 지정자는 데이터의 형식을 정의하는 중요한 역할을 합니다. 포맷 지정자는 각 데이터 유형에 맞는 값을 정확하게 처리할 수 있도록 도와주며, 다양한 데이터 타입을 출력하거나 읽을 때 반드시 맞춰 사용해야 합니다.

포맷 지정자의 주요 유형

  1. 정수형 포맷 지정자
  • %d 또는 %i: 10진 정수
  • %o: 8진수 정수
  • %x 또는 %X: 16진수 정수
  • %u: 부호 없는 10진 정수 예시:
   int num = 42;
   fprintf(file, "Decimal: %d, Hex: %x, Octal: %o\n", num, num, num);
  1. 실수형 포맷 지정자
  • %f: 부동소수점 실수 (기본적으로 소수점 표기)
  • %e 또는 %E: 지수 형식 (예: 1.23e+2)
  • %g 또는 %G: 가장 간결한 형식으로 실수 표현 (소수점 또는 지수) 예시:
   float pi = 3.14159;
   fprintf(file, "Pi: %.2f, Pi (e): %e\n", pi, pi);
  1. 문자형 포맷 지정자
  • %c: 단일 문자
  • %s: 문자열 예시:
   char ch = 'A';
   fprintf(file, "Character: %c, String: %s\n", ch, "Hello");
  1. 포인터형 포맷 지정자
  • %p: 포인터의 주소 출력 예시:
   int *ptr = &num;
   fprintf(file, "Pointer address: %p\n", (void*)ptr);
  1. 기타
  • %%: 문자 % 자체를 출력 예시:
   fprintf(file, "100%% complete\n");

포맷 지정자 활용 예시

예시 1: 다양한 데이터 출력

#include <stdio.h>

int main() {
    FILE *file = fopen("output.txt", "w");
    if (file != NULL) {
        int age = 30;
        float height = 175.5;
        char name[] = "Alice";
        fprintf(file, "Name: %s\nAge: %d\nHeight: %.1f cm\n", name, age, height);
        fclose(file);
    } else {
        printf("파일을 열 수 없습니다.\n");
    }
    return 0;
}

위 예제에서는 fprintf를 사용하여 문자열, 정수, 실수를 파일에 출력하는 방법을 보여줍니다.

예시 2: 날짜 출력

#include <stdio.h>

int main() {
    FILE *file = fopen("date.txt", "w");
    if (file != NULL) {
        int day = 25, month = 12, year = 2023;
        fprintf(file, "Date: %02d/%02d/%04d\n", day, month, year);
        fclose(file);
    } else {
        printf("파일을 열 수 없습니다.\n");
    }
    return 0;
}

이 예제에서는 날짜를 %%02d 포맷을 사용하여 2자리로 출력하고, 4자리로 연도를 출력합니다. 날짜 형식을 정확하게 맞추기 위한 포맷 지정자의 중요성을 강조하는 예입니다.

포맷 지정자 오류 방지

포맷 지정자와 데이터 유형이 일치하지 않으면 프로그램이 예기치 않게 동작할 수 있습니다. 예를 들어, %d 포맷으로 float 변수를 출력하면 예상치 못한 결과가 발생할 수 있습니다. 따라서 항상 포맷 지정자가 맞는 타입과 일치하는지 확인하는 것이 중요합니다.
파일 입출력 함수인 fprintffscanf를 사용할 때, 포맷 지정자나 파일 처리 과정에서 오류가 발생할 수 있습니다. 이런 오류를 미리 방지하거나, 발생한 오류를 처리하는 방법을 이해하는 것은 매우 중요합니다. 이 섹션에서는 fprintffscanf에서 발생할 수 있는 일반적인 오류와 그 해결 방법을 다루겠습니다.

1. 파일 열기 오류

가장 기본적인 오류 중 하나는 파일을 열 수 없는 경우입니다. 파일을 열기 전에 fopen 함수가 파일을 제대로 열었는지 확인해야 합니다. 만약 파일을 열지 못했다면, 파일 포인터는 NULL을 반환하며, 이를 체크하여 오류를 처리해야 합니다.

해결 방법: fopen 함수의 반환값을 확인

FILE *file = fopen("example.txt", "r");
if (file == NULL) {
    perror("파일을 열 수 없습니다");
    return 1;  // 파일 열기 실패 시 종료
}

2. 파일 쓰기 오류

fprintf를 사용할 때, 파일에 데이터를 쓸 수 없으면 오류가 발생할 수 있습니다. 예를 들어, 디스크 공간이 부족하거나, 파일이 읽기 전용으로 열려 있으면 데이터를 쓸 수 없습니다. 이런 오류를 확인하기 위해 fprintf의 반환값을 체크할 수 있습니다.

해결 방법: fprintf의 반환값 확인

int result = fprintf(file, "Hello, World!");
if (result < 0) {
    perror("파일에 쓸 수 없습니다");
    fclose(file);
    return 1;  // 파일 쓰기 오류 처리
}

3. 파일 읽기 오류

fscanf를 사용할 때, 지정한 포맷에 맞지 않는 데이터를 읽으려 하면 오류가 발생할 수 있습니다. 예를 들어, 숫자 대신 문자가 있는 위치에서 %d를 사용하면 fscanf는 데이터를 읽지 못하고 오류를 반환합니다.

해결 방법: fscanf의 반환값 확인

fscanf는 성공적으로 읽은 항목 수를 반환합니다. 포맷에 맞지 않는 데이터를 읽으려 하면 반환값이 0이 됩니다.

int age;
FILE *file = fopen("data.txt", "r");
if (file != NULL) {
    int result = fscanf(file, "%d", &age);
    if (result != 1) {
        printf("데이터 형식이 잘못되었습니다.\n");
        fclose(file);
        return 1;  // 잘못된 데이터 형식 오류 처리
    }
    printf("나이: %d\n", age);
    fclose(file);
} else {
    printf("파일을 열 수 없습니다.\n");
}

4. 파일 포인터가 NULL일 때 사용

파일 포인터가 NULL인 상태에서 파일 작업을 시도하면 오류가 발생합니다. 이는 주로 fopen 함수에서 실패할 경우 발생합니다. 파일을 열지 못한 상태에서 fprintffscanf를 호출하면 프로그램이 예기치 않게 종료될 수 있습니다.

해결 방법: 파일 포인터가 NULL인지 항상 확인

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

5. 포맷 지정자 오류

fprintffscanf에서 사용하는 포맷 지정자가 잘못되었을 때, 프로그램이 예상과 다른 동작을 할 수 있습니다. 예를 들어, 문자열을 읽는 %s를 사용하면서 입력이 너무 길거나, %d로 실수를 읽으려고 할 때 문제가 발생할 수 있습니다. 포맷 지정자는 항상 변수의 타입과 일치해야 하며, 그에 맞는 데이터를 정확하게 처리해야 합니다.

해결 방법: 포맷 지정자와 변수 타입 일치 확인

float height;
FILE *file = fopen("data.txt", "r");
if (file != NULL) {
    if (fscanf(file, "%f", &height) != 1) {
        printf("실수 형식 오류\n");
        fclose(file);
        return 1;  // 실수 읽기 오류 처리
    }
    printf("키: %.2f\n", height);
    fclose(file);
}

6. 파일 닫기 오류

파일을 열고 나서, 작업이 끝난 후 반드시 fclose로 파일을 닫아야 합니다. 파일을 제대로 닫지 않으면, 파일이 손상되거나 변경 사항이 반영되지 않을 수 있습니다. fclose 함수가 실패할 수도 있으므로 반환값을 확인하는 것이 좋습니다.

해결 방법: fclose의 반환값 확인

if (fclose(file) != 0) {
    printf("파일을 닫을 수 없습니다.\n");
    return 1;  // 파일 닫기 오류 처리
}

결론

fprintffscanf를 사용할 때 발생할 수 있는 오류는 대부분 파일 열기, 포맷 지정자 오류, 파일 읽기/쓰기 오류 등이 있습니다. 이러한 오류를 방지하고, 발생했을 때 적절히 처리하기 위해서는 각 함수의 반환값을 확인하고, 파일 포인터가 유효한지, 포맷이 맞는지 체크하는 것이 중요합니다. 오류 처리를 통해 프로그램의 안정성을 높일 수 있습니다.
fprintffscanf는 파일 입출력에서 매우 중요한 함수들이지만, 이를 제대로 활용하려면 다양한 활용 예시를 통해 이해를 깊게 할 필요가 있습니다. 이 섹션에서는 fprintffscanf를 실제 프로그램에 어떻게 적용할 수 있는지, 실습 예시를 통해 살펴보겠습니다. 이를 통해 사용자는 이 함수들의 활용 범위를 확장하고, 다양한 파일 입출력 시나리오에 대응할 수 있습니다.

예시 1: 성적 데이터 저장 및 읽기

학생들의 성적을 저장한 후, 이를 파일에서 읽어오는 프로그램을 작성해 보겠습니다. 성적은 이름과 점수로 구성되어 있으며, fprintffscanf를 사용해 데이터를 파일에 기록하고 읽어옵니다.

성적 데이터를 파일에 저장하기

#include <stdio.h>

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

    char name[50];
    int score;

    printf("학생 이름과 성적을 입력하세요 (종료하려면 'q' 입력):\n");
    while (1) {
        printf("이름: ");
        scanf("%s", name);
        if (name[0] == 'q') break;  // 'q' 입력 시 종료

        printf("성적: ");
        scanf("%d", &score);
        fprintf(file, "Name: %s, Score: %d\n", name, score);
    }

    fclose(file);
    printf("성적 데이터가 저장되었습니다.\n");
    return 0;
}

이 코드는 사용자가 이름과 성적을 입력하면 이를 scores.txt 파일에 기록합니다. fprintf를 사용하여 포맷에 맞춰 이름과 성적을 파일에 저장합니다.

성적 데이터를 파일에서 읽어오기

#include <stdio.h>

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

    char name[50];
    int score;

    printf("저장된 성적 데이터:\n");
    while (fscanf(file, "Name: %s, Score: %d\n", name, &score) == 2) {
        printf("Name: %s, Score: %d\n", name, score);
    }

    fclose(file);
    return 0;
}

이 코드는 scores.txt 파일에서 성적 데이터를 읽어와서 화면에 출력합니다. fscanf를 사용하여 파일에서 데이터를 읽고, 이를 적절한 변수에 저장하여 출력합니다.

예시 2: CSV 파일 처리

CSV 파일은 데이터를 쉼표로 구분하여 저장하는 형식입니다. fprintffscanf를 사용하여 CSV 파일을 읽고 쓰는 예제를 살펴보겠습니다.

CSV 파일에 데이터 저장하기

#include <stdio.h>

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

    fprintf(file, "Name,Age,Grade\n");  // CSV 헤더 작성
    fprintf(file, "Alice,21,A\n");
    fprintf(file, "Bob,22,B\n");
    fprintf(file, "Charlie,23,C\n");

    fclose(file);
    printf("CSV 파일에 데이터가 저장되었습니다.\n");
    return 0;
}

위 코드에서는 data.csv라는 파일에 Name, Age, Grade라는 헤더와 함께 학생들의 정보를 기록합니다. 각 데이터는 쉼표로 구분되어 있습니다.

CSV 파일에서 데이터 읽기

#include <stdio.h>

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

    char name[50];
    int age;
    char grade[2];

    // 첫 번째 줄은 헤더이므로 건너뜁니다
    fscanf(file, "%*s,%*s,%*s\n");

    while (fscanf(file, "%49[^,],%d,%1s\n", name, &age, grade) == 3) {
        printf("Name: %s, Age: %d, Grade: %s\n", name, age, grade);
    }

    fclose(file);
    return 0;
}

이 코드는 data.csv 파일에서 데이터를 읽어와서 각 학생의 이름, 나이, 학점을 출력합니다. fscanf의 포맷을 사용하여 쉼표로 구분된 데이터를 읽어 변수에 저장합니다.

예시 3: 로그 파일 처리

서버에서 발생한 로그를 기록하고, 이를 분석하는 예시입니다. 로그 파일에 각 요청의 시간과 상태 코드를 기록한 후, 이를 읽어 분석하는 프로그램을 작성합니다.

로그 파일에 데이터 저장하기

#include <stdio.h>
#include <time.h>

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

    time_t now;
    struct tm *tm_info;
    char buffer[26];

    // 로그 작성
    time(&now);
    tm_info = localtime(&now);
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_info);
    fprintf(file, "%s - Status: 200 OK\n", buffer);

    time(&now);
    tm_info = localtime(&now);
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_info);
    fprintf(file, "%s - Status: 404 Not Found\n", buffer);

    fclose(file);
    printf("로그가 기록되었습니다.\n");
    return 0;
}

로그 파일에서 데이터 읽기

#include <stdio.h>

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

    char timestamp[26];
    char status[50];

    while (fscanf(file, "%25s - Status: %49[^\n]\n", timestamp, status) == 2) {
        printf("Time: %s, Status: %s\n", timestamp, status);
    }

    fclose(file);
    return 0;
}

결론

이러한 예시들을 통해 fprintffscanf의 다양한 활용법을 배울 수 있습니다. 파일에 데이터를 저장하고 읽어오는 과정에서 포맷 지정자 사용을 잘 이해하고, 적절한 오류 처리와 함께 효율적인 파일 입출력 작업을 할 수 있습니다.

목차
  1. 요약

요약


본 기사에서는 C언어에서 파일 입출력 함수인 fprintffscanf를 사용하여 데이터를 파일에 기록하고 읽어오는 방법을 다뤘습니다. fprintf를 이용한 파일에 데이터 쓰기, fscanf를 활용한 파일에서 데이터 읽기 방법을 설명하였으며, 각 함수에서 발생할 수 있는 오류를 처리하는 방법도 함께 소개했습니다. 또한, 다양한 실습 예시를 통해 CSV 파일, 성적 데이터, 로그 파일 처리 등의 실제 응용 사례를 제시하여, 파일 입출력의 중요성과 이를 적절히 활용하는 방법을 배울 수 있었습니다.

fprintffscanf는 파일과의 데이터 교환을 효율적으로 처리할 수 있는 강력한 도구입니다. 이러한 함수들을 제대로 활용하고, 발생할 수 있는 오류를 예방하는 방법을 익히면, 파일 입출력 작업이 원활해지고, 안정적인 프로그램을 작성할 수 있습니다.

목차
  1. 요약