파일 입출력
지금까지는 프로그램을 실행하는 동안에만 데이터가 존재했습니다. printf로 화면에 출력한 내용도, malloc으로 할당해둔 메모리도 프로그램이 종료되면 모두 사라져 버립니다. 그런데 실제로 우리가 쓰는 프로그램들은 대부분 저장 기능을 갖고 있죠. 메모장에 적은 글, 게임의 저장 데이터, 설정 파일 등은 프로그램을 껐다 켜도 그대로 남아 있습니다. 이렇게 데이터를 디스크에 영구적으로 저장하고 불러오는 기능을 C언어에서는 파일 입출력이라고 부릅니다. 오늘은 FILE 포인터와 fopen, fprintf, fscanf, fclose 같은 기본 함수들을 이용해서 파일을 다루는 방법을 알아보겠습니다.
파일을 다루기 위한 기본 흐름
C언어에서 파일을 다루는 과정은 크게 세 단계로 이루어집니다. 먼저 fopen으로 파일을 열고, 그 다음 fprintf나 fscanf 같은 함수로 파일에 쓰거나 파일에서 읽고, 마지막으로 fclose로 파일을 닫아줍니다. 파일을 열 때는 stdio.h에 정의된 FILE 타입의 포인터가 필요하고, 어떤 모드로 열지도 함께 지정해줘야 합니다. 대표적인 모드로는 쓰기 모드인 "w", 읽기 모드인 "r", 이어쓰기 모드인 "a"가 있습니다.
파일에 데이터 쓰기
먼저 파일에 문자열과 숫자를 저장하는 예제를 살펴보겠습니다.
#include <stdio.h>
int main(void) {
FILE *fp;
fp = fopen("scores.txt", "w");
if (fp == NULL) {
printf("파일을 열 수 없습니다.\n");
return 1;
}
fprintf(fp, "%s %d\n", "철수", 90);
fprintf(fp, "%s %d\n", "영희", 85);
fprintf(fp, "%s %d\n", "민수", 78);
fclose(fp);
printf("scores.txt 파일에 저장을 완료했습니다.\n");
return 0;
}
이 프로그램을 실행하면 화면에는 아래처럼 출력됩니다.
scores.txt 파일에 저장을 완료했습니다.
그리고 프로그램과 같은 폴더에 scores.txt라는 파일이 새로 생기고, 그 안에는 다음과 같은 내용이 저장됩니다.
철수 90
영희 85
민수 78
fopen("scores.txt", "w")는 scores.txt라는 파일을 쓰기 모드로 여는데, 만약 해당 파일이 이미 있다면 내용을 모두 지우고 새로 쓰기 시작합니다. fprintf는 우리가 익숙한 printf와 사용법이 똑같지만, 첫 번째 인자로 어디에 출력할지를 나타내는 FILE 포인터를 받는다는 점이 다릅니다. 즉 printf가 화면(표준 출력)에 쓰는 함수라면, fprintf는 지정한 파일에 쓰는 함수라고 생각하면 됩니다.
파일에서 데이터 읽어오기
이번에는 방금 저장한 scores.txt 파일을 다시 읽어서 화면에 출력하는 예제입니다.
#include <stdio.h>
int main(void) {
FILE *fp;
char name[20];
int score;
fp = fopen("scores.txt", "r");
if (fp == NULL) {
printf("파일을 열 수 없습니다.\n");
return 1;
}
printf("=== 저장된 점수 목록 ===\n");
while (fscanf(fp, "%s %d", name, &score) != EOF) {
printf("%s : %d점\n", name, score);
}
fclose(fp);
return 0;
}
이 코드를 실행하면 다음과 같은 결과를 확인할 수 있습니다.
=== 저장된 점수 목록 ===
철수 : 90점
영희 : 85점
민수 : 78점
fscanf는 scanf와 마찬가지로 형식 문자열에 맞춰 값을 읽어오는데, 읽어올 대상이 키보드가 아니라 파일이라는 점이 다릅니다. fscanf는 더 이상 읽을 데이터가 없으면 EOF(End Of File, 파일의 끝을 의미하는 값)를 반환하므로, while 문의 조건으로 사용하면 파일 끝까지 한 줄씩 자연스럽게 읽어올 수 있습니다.
코드 동작 설명 및 예상 실행 결과
- 첫 번째 예제는
"w"모드로 파일을 열어 세 명의 이름과 점수를 한 줄씩 저장하고 파일을 닫습니다. - 두 번째 예제는
"r"모드로 같은 파일을 열어fscanf로 한 줄씩 읽어 들이면서,EOF를 만날 때까지 반복해서 화면에 출력합니다. - 두 예제 모두
fopen의 반환값이NULL인지 확인하는 과정을 거치는데, 파일이 없거나 권한 문제로 열리지 않는 경우를 대비하기 위한 필수적인 습관입니다. - 파일을 다 사용한 뒤에는
fclose를 반드시 호출해야 합니다. 이를 생략하면 버퍼에 남아있던 내용이 파일에 제대로 기록되지 않거나, 시스템이 열 수 있는 파일 개수 제한에 걸릴 수 있습니다.
핵심 요약
- 파일 입출력을 하려면
stdio.h의FILE포인터를 이용해fopen으로 파일을 열고, 작업이 끝나면fclose로 닫아야 합니다. fopen의 두 번째 인자는 파일을 여는 모드를 뜻하며,"w"는 쓰기(덮어쓰기),"r"은 읽기,"a"는 이어쓰기입니다.fprintf와fscanf는 각각printf,scanf와 사용법이 비슷하지만, 화면이 아니라 지정한 파일을 대상으로 동작합니다.fscanf는 더 이상 읽을 내용이 없으면EOF를 반환하므로, 반복문 조건으로 활용하면 파일 전체를 편리하게 읽을 수 있습니다.fopen이 실패해NULL을 반환할 수 있으므로, 파일을 열자마자 반환값을 확인하는 습관을 들이는 것이 좋습니다.
다음 편에서는 코드를 컴파일하기 전에 미리 처리되는 “전처리기와 매크로”를 주제로 다뤄보겠습니다.