프린세스 다이어리

[C] 파일 입출력이 왜 필요한가요? C언어로 파일 입출력 해보기 본문

C, C++

[C] 파일 입출력이 왜 필요한가요? C언어로 파일 입출력 해보기

개발공주 2021. 10. 1. 12:40
728x90

1. 파일 입출력의 필요성

 

프로그램이 꺼진 이후에도 데이터를 저장하기 위해서는 파일 입출력이 필요하다. 예를 들어, 게임을 껐다 켰는데 다시 캐릭터를 처음부터 만들어야 한다면 시간 버린 느낌이 들 것이다. 즉, 어떠한 데이터를 프로그램 안에서만 사용하는 게 아니라, 프로그램의 외부에 일시적으로 저장을 해 놨다가 프로그램을 다시 실행시킬 때 다시 불러올 수 있도록 하는 역할을 한다.

 

흔히 SSD, RAM, CPU는 일반적으로 컴퓨터에 들어가는 부품이다. 이 중 파일이 실질적으로 저장이 되는 위치를 고르라면, 바로 SSD다. 컴퓨터 아키텍처마다 조금씩 차이는 있겠지만 램과 CPU는 휘발성이 있다는 특징이 있다. 그래서 바탕화면 등에 있는 파일은 SSD와 같은 보조기억장치에 저장되고, 그것을 더블클릭해서 실행하면 프로그램이 프로세스로 전환되면서 램에 들어가서 구동을 하게 되고, CPU가 그것을 하나씩 읽어서 구동을 하게 되는 것이다. 

 

2. 파일 입출력 방법

 

FILE *fp;
fp = fopen(파일 경로, 접근 방식);
fclose(fp);

파일 입출력 변수는 FILE 형식의 포인터 변수로 선언한다. 파일을 열 때는 fopen() 함수를, 파일을 닫을 때는 fclose() 함수를 이용한다.  열 때는 특정한 경로에서 파일을 열고, 파일에 접근할 수 있는 포인터 주소를 반환하게 된다. 그것을 파일 입출력 변수 *fp에 담아서, *fp를 통해 파일에 접근하고 어떠한 작업을 할 수 있는 것이다. 

 

2-1. 파일 열기 함수, fopen() 

 

fopen() 함수에는 파일 경로와 접근 방식을 설정하여 파일을 열 수 있다. 기본 경로는 현재 프로그램이 실행되고 있는 경로고, 가장 많이 사용되는 접근 방식은 r, w, a이다.

r(read) 파일에 접근하여 데이터를 읽는다.
w(write) 파일에 접근하여 데이터를 기록한다.(파일이 이미 존재하면 덮어쓰기)
a(add) 파일에 접근하여 데이터를 뒤에서부터 기록한다.

 

2-2. 파일 입출력 함수, fprintf(), fscanf()

 

이전까지 기본적인 사용자 입출력 연습을 위해 printf()와 scanf() 함수를 사용했었다. 파일 입출력을 위해서는 fprintf()와 fscanf()가 사용된다.

fprintf(파일 포인터, 서식, 형식 지정자);
fscanf(파일 포인터, 서식, 형식 지정자);

파일 포인터가 첫 번째 매개변수로 추가된 형태다. 

 

파일 입출력은 열고, 읽고/쓰고, 닫기의 과정을 철저히 따라야 한다. 파일을 열 때는 파일 포인터가 사용되며, 이는 동적으로 할당된 것이다. 따라서 파일 처리 이후에 파일을 닫아두지 않으면 메모리 누수가 발생해서 언젠가는 프로그램이 갑자기 종료가 될 수도 있다. 

 

#include <stdio.h> 

int main(void) {
    char s[20] = "hello eunjin";
    FILE *fp;
    fp = fopen("temp.txt", "w");
    fprintf(fp, "%s\n", s);
    fclose(fp);
}

fopen() 함수로 하나의 파일을 열어 주고, 거기에 접근할 수 있는 메모리 주소 값을 fp에 담아주었다. 이후에는 fprintf()를 이용해서 파일 포인터에 접근해서 하나의 문자열을 쓰도록 하는 것이다. 이후에 파일 처리가 끝나면 닫아주는 것이다. 이 프로그램을 실행하고 나면, 이 프로그램이 실행되고 있는 디렉터리에 temp.txt 파일이 생기고 그 안에 "hello eunjin"이라는 문자열이 기록된 상태가 된다. 

 

3. 학생 정보 시스템 만들기 미니 프로젝트

 

3-1. 학생들의 이름과 성적 파일 만들기

 

학생에 대한 정보가 기록되어 있는 데이터 파일을 만든다. 프로그램이 실행되고 있는 디렉터리에서 폴더를 하나 파, 그 폴더 안에 input.txt 파일을 만들어 다음과 같은 내용을 입력하고 저장해 두었다. 맨 위 숫자는 학생의 숫자고, 다음은 학생의 이름과 점수가 공백을 두고 입력돼 있다. 

6
이슬아 97
최지혜 92
권혜진 96
조민주 93
한승연 95
이은진 94

 

3-2. 학생 구조체 만들기

 

특정 구조체 형태를 가진 학생의 속성들을 프로그램 상에서 처리할 수 있도록 만들어 둔다. 학생의 이름과 점수가 담길 변수를 만들어 준다.

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

typedef struct {
    char name[20];
    int score;
} Student;

 

3-3. 학생의 데이터를 읽어 그대로 출력하기

 

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

typedef struct {
    char name[20];
    int score;
} Student;


int main(void) {
    int n, sum = 0;
    FILE *fp;
    fp = fopen("./tutorials/input.txt", "r");
    fscanf(fp, "%d", &n);

    Student *students = (Student*) malloc(sizeof(Student) * n);

    for (int i = 0; i < n; i++)
    {
        fscanf(fp, "%s %d", &((students + i) -> name), &((students + i) -> score));
        printf("이름: %s / 성적: %d \n", (students + i) -> name, (students + i) -> score);
    } 
}

input.txt 파일을 읽기 모드로 열어서 scanf로 하나의 정수형 변수를 읽어 들이도록 한다. 그리고 학생이 몇 명인지 모르기 때문에 동적 메모리 할당을 통해서, 총 n명의 학생에 대한 정보가 담길 수 있는 메모리 공간을 할당한다. 그리고 학생에 대한 정보를 하나씩 for문을 돌면서 입력을 받아서 출력해주는 로직이다.

 

main.c:21:29: warning: format specifies type 'char *' but the argument has type 'char (*)[20]' [-Wformat]
        fscanf(fp, "%s %d", &((students + i) -> name), &((students + i) -> score));
                    ~~      ^~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
이름: 이슬아 / 성적: 97 
이름: 최지혜 / 성적: 92 
이름: 권혜진 / 성적: 96 
이름: 조민주 / 성적: 93 
이름: 한승연 / 성적: 95 
이름: 이은진 / 성적: 94

 

3-4. 성적 평균 구하고, 메모리 할당 해제하기

 

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

typedef struct {
    char name[20];
    int score;
} Student;


int main(void) {
    int n, sum = 0;
    FILE *fp;
    fp = fopen("./tutorials/input.txt", "r");
    fscanf(fp, "%d", &n);

    Student *students = (Student*) malloc(sizeof(Student) * n);

    for (int i = 0; i < n; i++)
    {
        fscanf(fp, "%s %d", &((students + i) -> name), &((students + i) -> score));
    }

    for (int i = 0; i < n; i++)
    {
        sum += (students + i) -> score;
    }

    free(students);
    fclose(fp);

    printf("점수 평균: %.2f \n", (double) sum / n);
}
main.c:21:29: warning: format specifies type 'char *' but the argument has type 'char (*)[20]' [-Wformat]
        fscanf(fp, "%s %d", &((students + i) -> name), &((students + i) -> score));
                    ~~      ^~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
점수 평균: 94.50

학생 구조체의 포인터에 하나씩 접근해서 score를 모두 더해주고 난 다음에는 메모리 할당이 필요 없어졌으므로 free() 함수로 메모리를 해제해 주고, 파일을 닫아 준다. 그리고 점수 평균을 소수점 둘째 자리까지 표현해 준다. 

 

printf("점수 평균: %.2f \n", (double) sum / n);

합계의 형을 double 형으로 변환해 주고, 그 값을 소숫점 둘째 자리까지의 실수로 출력해주는 로직이다. 성공적으로 점수 평균이 출력이 된다. 

728x90
Comments