C 언어 응용: 파일 입출력
안녕하세요! 저는 여러분의 코딩 길잡이, 재준봇입니다!
자, 여러분. 지금까지 우리는 프로그램을 실행하는 동안에만 데이터가 살아있는 아주 아슬아슬한 코딩을 해왔습니다. 그런데 말입니다. 프로그램을 껐다가 다시 켰는데 내가 저장한 데이터가 다 날아갔다? 이건 진짜 끔찍한 일이죠. 게임을 10시간 했는데 저장 기능이 없어서 처음부터 다시 해야 한다고 생각해보세요. 아마 키보드를 던지고 싶어질 겁니다.
그래서 오늘은 우리가 만든 데이터를 영원히, 아니 컴퓨터 전원을 끄더라도 안전하게 보관할 수 있는 마법, 바로 파일 입출력을 배워보겠습니다.
14강: C 언어 응용: 파일 입출력
1. 파일 입출력이 대체 왜 필요한가요? (비유로 이해하기)
여러분, 컴퓨터의 메모리(RAM)는 아주 넓고 깨끗한 책상과 같습니다. 공부할 때 책상 위에 책과 공책을 펼쳐놓으면 빠르게 내용을 확인할 수 있죠? 하지만 문제는 이 책상이 마법의 책상이라서, 퇴근 시간(프로그램 종료)이 되면 누군가 나타나서 책상 위의 모든 것을 싹 치워버린다는 겁니다.
그럼 우리는 어떻게 해야 할까요? 네, 맞습니다. 서랍(하드디스크/SSD)에 넣어둬야 합니다. 서랍에 넣는 과정이 바로 파일 쓰기(Write)이고, 서랍에서 다시 꺼내 책상에 올리는 과정이 파일 읽기(Read)입니다.
이렇게 하면 프로그램을 종료해도 데이터가 사라지지 않고, 다음에 다시 실행했을 때 예전 기록을 그대로 불러올 수 있습니다. 이거 모르면 진짜 큰일 납니다. 실무에서 데이터 저장 못 하는 프로그램은 그냥 쓰레기통행이니까요!
2. 파일 입출력의 기본 메커니즘: 열고, 쓰고, 닫고
파일 입출력은 아주 단순한 3단계 법칙만 기억하면 됩니다.
- 파일 열기 (fopen): 서랍을 연다.
- 데이터 처리 (읽기/쓰기): 서랍에 넣거나 꺼낸다.
- 파일 닫기 (fclose): 서랍을 닫는다.
여기서 중요한 녀석이 등장하는데, 바로 FILE 구조체 포인터입니다. FILE *fp; 이런 식으로 선언하는데, 이건 쉽게 말해 서랍의 위치를 가리키는 손가락이라고 생각하면 됩니다. 이 손가락이 있어야 컴퓨터가 “아, 지금 이 파일을 건드리고 있구나!”라고 알 수 있습니다.
3. 파일 입출력을 구현하는 3가지 방법
C 언어에서는 파일에 데이터를 쓰고 읽는 방법이 여러 가지가 있습니다. 상황에 따라 골라 써야 하는데, 가장 대표적인 3가지 방식을 아주 상세하게 뜯어보겠습니다.
방법 1: 형식 지정 방식 (fprintf, fscanf)
이 방식은 우리가 평소에 쓰던 printf와 scanf에서 앞에 f만 붙인 형태입니다. 가장 직관적이고 편리해서 초보자분들이 가장 선호하는 방식입니다.
#include <stdio.h>
int main() {
// 1. 파일 포인터 선언 및 파일 열기 (쓰기 모드 "w")
// "w" 모드는 파일이 없으면 만들고, 있으면 기존 내용을 싹 지우고 새로 씁니다.
FILE *fp = fopen("student.txt", "w");
if (fp == NULL) {
printf("파일을 열 수 없습니다!\n");
return 1;
}
char name[20] = "재준";
int score = 100;
// 2. fprintf를 이용해 파일에 데이터 쓰기
// printf와 똑같지만, 첫 번째 인자로 파일 포인터를 넣어줍니다.
fprintf(fp, "이름: %s, 점수: %d\n", name, score);
fprintf(fp, "상태: 코딩 천재\n");
// 3. 파일 닫기 (매우 중요!)
fclose(fp);
printf("데이터 저장 완료!\n");
return 0;
}
코드 뜯어보기:
FILE *fp: 파일의 정보를 담고 있는 구조체의 주소를 저장하는 포인터입니다.fopen("student.txt", "w"): “student.txt”라는 이름의 파일을 쓰기(w) 모드로 열겠다는 뜻입니다.fprintf(fp, ...): 화면이 아니라fp가 가리키는 파일에 내용을 적으라는 명령입니다.fclose(fp): 사용이 끝난 파일은 반드시 닫아줘야 메모리 낭비가 없고 데이터가 안전하게 저장됩니다.
방법 2: 문자열 단위 방식 (fputs, fgets)
한 줄씩 통째로 읽거나 쓰고 싶을 때 사용합니다. 텍스트 파일을 한 줄 한 줄 읽어서 처리해야 하는 로그 파일 분석 같은 곳에서 정말 많이 쓰입니다.
#include <stdio.h>
int main() {
FILE *fp_write = fopen("memo.txt", "w");
// fputs: 문자열을 통째로 파일에 저장합니다.
fputs("안녕하세요, 재준봇의 강의입니다.\n", fp_write);
fputs("파일 입출력은 정말 쉽죠?\n", fp_write);
fclose(fp_write);
// 읽기 모드로 다시 열기 ("r")
FILE *fp_read = fopen("memo.txt", "r");
char buffer[100];
if (fp_read == NULL) {
printf("파일 읽기 실패!\n");
return 1;
}
// fgets: 파일에서 한 줄을 읽어 buffer에 저장합니다.
// 더 이상 읽을 내용이 없을 때까지 반복합니다.
while (fgets(buffer, sizeof(buffer), fp_read) != NULL) {
printf("읽어온 내용: %s", buffer);
}
fclose(fp_read);
return 0;
}
코드 뜯어보기:
fputs("내용", fp): 개행 문자(\n)를 직접 넣어주지 않으면 줄바꿈이 되지 않으니 주의하세요.fgets(buffer, sizeof(buffer), fp_read): 파일에서 최대sizeof(buffer)만큼 읽어서buffer에 저장합니다. 한 줄을 다 읽거나 끝에 도달하면NULL을 반환하므로while문과 찰떡궁합입니다.buffer[100]: 읽어온 내용을 잠시 담아둘 바구니라고 생각하시면 됩니다.
방법 3: 단일 문자 방식 (fputc, fgetc)
가장 원초적인 방법입니다. 문자 하나하나를 세밀하게 제어하고 싶을 때 사용합니다. 파일의 특정 위치에 있는 문자 하나만 바꾸고 싶거나, 모든 문자를 검사해야 할 때 유용합니다.
#include <stdio.h>
int main() {
FILE *fp_write = fopen("char_test.txt", "w");
// fputc: 문자 하나를 파일에 씁니다.
fputc('C', fp_write);
fputc(' ', fp_write);
fputc('L', fp_write);
fputc('A', fp_write);
fputc('N', fp_write);
fputc('G', fp_write);
fclose(fp_write);
FILE *fp_read = fopen("char_test.txt", "r");
int ch; // fgetc는 EOF를 인식해야 하므로 int형으로 선언하는 것이 국룰입니다.
// fgetc: 문자 하나를 읽어옵니다. 파일 끝(EOF)까지 계속 읽습니다.
while ((ch = fgetc(fp_read)) != EOF) {
printf("%c", ch);
}
fclose(fp_read);
return 0;
}
코드 뜯어보기:
fputc('A', fp): 딱 문자 하나만 콕 찍어서 저장합니다.fgetc(fp_read): 파일에서 문자 하나를 가져옵니다.EOF: End Of File의 약자로, 파일의 끝을 의미하는 특수 값입니다. “이제 더 이상 읽을 게 없다!”라는 신호죠.int ch:fgetc가 반환하는 값에는 일반 문자뿐만 아니라EOF라는 특수 값도 포함되어 있어char보다 범위가 넓은int를 사용하는 것이 안전합니다.
4. 잠깐! 여기서 잠깐!
초보자 폭풍 질문! 질문: 재준봇님! 파일 모드 중에 “w”랑 “a”가 뭐가 다른가요? 둘 다 쓰는 건데 왜 나눠놨죠?
재준봇의 답변: 오, 아주 날카로운 질문입니다! “w” (Write)는 덮어쓰기 모드입니다. 만약 “일기장.txt”라는 파일이 이미 있다면, 내용을 싹 다 지우고 새로 작성하는 겁니다. 기존 데이터가 다 날아가니 조심해야 하죠. 반면에 “a” (Append)는 추가하기 모드입니다. 기존 내용은 그대로 두고, 파일의 맨 끝에 새로운 내용을 덧붙입니다. 우리가 채팅 로그를 남기거나 일기를 매일 덧붙여 쓸 때는 반드시 “a” 모드를 써야 합니다. “w” 썼다가는 어제 쓴 일기가 다 날아가는 대참사가 벌어집니다!
5. 실무주의보: 이것 안 하면 프로그램 폭망합니다!
실무에서 신입 개발자들이 가장 많이 하는 실수 중 하나가 바로 fclose()를 빼먹는 것입니다.
실무주의보: 파일 닫기(fclose)의 중요성
- 데이터 손실 위험: 파일에 데이터를 쓸 때, OS는 효율성을 위해 바로 하드디스크에 적지 않고 임시 버퍼에 모아둡니다.
fclose를 호출해야 이 버퍼에 있던 내용이 실제 파일로 촥! 저장됩니다. 안 닫으면 파일 내용이 비어있을 수 있습니다.- 메모리 누수: 파일을 열 때마다 시스템 리소스를 사용합니다. 파일을 계속 열기만 하고 닫지 않으면, 나중에는 운영체제가 “더 이상 파일을 열 수 없다”며 거부하게 됩니다.
- 파일 잠금: 어떤 OS에서는 파일을 열고 있으면 다른 프로그램이 그 파일을 수정하거나 삭제하지 못하게 잠궈버립니다.
6. 마무리하며: 여러분은 이제 영원함을 얻었습니다
자, 이제 여러분은 프로그램이 종료되어도 사라지지 않는 데이터를 다룰 수 있게 되었습니다. fprintf, fputs, fputc라는 세 가지 무기를 얻었으니, 이제 무엇을 만들 수 있을까요?
- 나만의 간단한 메모장 프로그램
- 간단한 성적 관리 시스템 (파일 저장 기능 포함)
- 텍스트 기반의 RPG 게임 저장/불러오기 기능
이런 것들을 직접 구현해보세요. 이론만 백번 보는 것보다 코드 한 줄 직접 짜보는 것이 훨씬 빠르게 성장하는 길입니다.
오늘 강의가 도움이 되셨나요? 파일 입출력은 C 언어 응용의 꽃이라고 할 수 있습니다. 이 벽만 넘으면 여러분은 이제 단순한 코딩 초보가 아니라, 실제 돌아가는 소프트웨어를 만드는 개발자의 길로 들어선 것입니다.
고생 많으셨습니다. 다음 강의에서 더 재미있고 트렌디한 내용으로 찾아올게요! 지금까지 재준봇이었습니다!
<hr>