파일 입출력
이번 포스팅은 간단한 사용자 정보(이름, 나이, 이메일)를 CSV 형식으로 txt 파일에 저장하고, 저장된 파일을 다시 읽어와서 화면에 출력하는 예제를 다룬다. 실제 C언어에서 파일 입출력을 사용한 적은 크게 없다. 하지만 파일이 쓰고 읽히는 기본 원리를 이해하기 위해 작성하게 되었다.
⭐ 1. 파일 쓰기 예제
먼저 사용자 정보를 txt 파일에 저장하는 예제부터 시작한다. 파일에 데이터를 쓰기 위해 fprintf 함수를 사용한다. 각 필드는 쉼표로 구분하고, 각 레코드는 새로운 줄에 위치한다.
#include <stdio.h>
int main() {
// 파일 포인터 선언
FILE *fp = fopen("test.txt", "w");
if (fp == NULL) {
perror("파일 열기 실패");
return -1;
}
// 데이터 쓰기 (이름, 나이, 이메일)
fprintf(fp, "이름,나이,이메일\n"); // 헤더 작성
fprintf(fp, "홍길동,30,hong@example.com\n");
fprintf(fp, "김철수,25,kim@example.com\n");
// 파일 닫기
fclose(fp);
printf("데이터가 성공적으로 저장되었습니다.\n");
return 0;
}
모드
|
설명
|
r
|
읽기 전용으로 파일 열기. 파일이 존재하지 않으면 열기 실패.
|
w
|
쓰기 전용으로 파일 열기. 파일이 이미 존재하면 내용을 지우고, 없으면 새 파일 생성.
|
a
|
추가 모드로 파일 열기. 파일이 이미 존재하면 데이터를 파일 끝에 추가하고, 없으면 새 파일 생성.
|
r+
|
읽기/쓰기 모드로 파일 열기. 파일이 존재하지 않으면 열기 실패.
|
w+
|
읽기/쓰기 모드로 파일 열기. 파일이 이미 존재하면 내용을 지우고, 없으면 새 파일 생성.
|
a+
|
추가/읽기 모드로 파일 열기. 파일 끝에 데이터를 추가하고, 파일이 없으면 새 파일 생성.
|
rb, wb, ab, rb+, wb+, ab+
|
위 모드들에 b를 추가하여 이진 파일로 작업할 때 사용. 예: rb는 이진 파일을 읽기 전용으로 열기.
|
이 코드는 test.txt라는 파일을 생성하고, 파일에 이름, 나이, 이메일 정보를 쉼표로 구분하여 차례로 파일에 쓴다. 위 코드에서 중요한 포인트는 순서이다. 우선, 파일을 열어야 한다. 파일이 열렸다면 이제는 파일에 읽기 쓰기를 할 준비가 된 셈이다. 그리고 데이터를 순서대로 파일에 쓴다. 쓰기 작업이 마무리가 되었다면, 꼭 파일을 닫아야 한다. 제대로 닫지 않은 상태에서 프로그램을 종료한다면 파일이 깨지거나 올바르게 저장되지 않을 수 있다. 이 원리는 c언어는 고사하고 파이썬, 자바와 동일하니 꼭 기억해야 한다.
⭐ 2. 파일 읽기 예제
다음으로 저장된 txt 파일을 읽어와 콘솔에 출력한다. 파일로부터 데이터를 읽기 위해 fscanf 함수를 사용한다. 파일의 끝에 도달할 때까지 반복하여 데이터를 읽는다.
#include <stdio.h>
int main() {
// 파일 포인터 선언
FILE *fp = fopen("users.csv", "r");
if (fp == NULL) {
perror("파일 열기 실패");
return -1;
}
char name[100], email[100];
int age;
// 헤더 읽기
fscanf(fp, "%*s %*s %*s\n"); // 첫 번째 줄(헤더)는 건너뜁니다.
// 데이터 읽기 (이름, 나이, 이메일)
while (fscanf(fp, "%[^,],%d,%[^,\n]\n", name, &age, email) != EOF) {
printf("이름: %s, 나이: %d, 이메일: %s\n", name, age, email);
}
// 파일 닫기
fclose(fp);
return 0;
}
⭐ 3. 주의사항
◆ 잘못된 예시 코드
#include <stdio.h>
int main() {
FILE *file;
char buffer[100];
// 파일 열기 (파일 포인터 NULL 체크 없음)
file = fopen("example.txt", "r");
// 파일로부터 데이터 읽기 (파일 포인터가 NULL인지 확인하지 않음)
fscanf(file, "%s", buffer);
printf("파일 내용: %s\n", buffer);
// 파일 닫기 (fclose 없음)
return 0;
}
위 코드는 다음과 같은 문제가 있다:
파일 포인터 NULL 검사 없음: fopen() 함수가 실패할 경우 file 포인터는 NULL이 되는데, 이를 확인하지 않으면 프로그램이 비정상적으로 동작할 수 있다.
파일 닫기(fclose) 없음: 파일 작업이 끝난 후 fclose()를 호출하지 않으면 파일 리소스가 반환되지 않아 메모리 누수가 발생할 수 있다.
◇ 올바른 예시 코드
아래는 파일을 성공적으로 열었는지 확인하고, 작업 후 파일을 제대로 닫는 수정된 코드이다.
#include <stdio.h>
int main() {
FILE *file;
char buffer[100];
// 파일 열기
file = fopen("example.txt", "r");
// 파일 포인터 NULL 검사
if (file == NULL) {
perror("파일 열기 오류");
return 1; // 파일 열기에 실패한 경우 프로그램 종료
}
// 파일로부터 데이터 읽기
if (fscanf(file, "%99s", buffer) == 1) {
printf("파일 내용: %s\n", buffer);
} else {
printf("파일 읽기 실패 또는 빈 파일입니다.\n");
}
// 파일 닫기
fclose(file);
return 0;
}
이 수정된 코드는 다음과 같은 점에서 수정되었다.
- 파일 포인터 NULL 검사: fopen()이 실패할 경우 file이 NULL이 되므로, if (file == NULL)을 통해 파일이 제대로 열렸는지 확인한다. 실패 시 오류 메시지를 출력하고 프로그램을 종료한다.
- fclose 사용: 파일 작업이 끝난 후 반드시 fclose(file)을 호출하여 파일을 닫고, 리소스를 반환한다.
- 입력 데이터 제한: fscanf(file, "%99s", buffer)로 최대 입력 크기를 지정하여 버퍼 오버플로우를 방지했다.
이 코드는 파일 작업을 안전하게 처리하여 파일 입출력 중 발생할 수 있는 오류를 방지한다.
'C언어 30강' 카테고리의 다른 글
[C/C++ Tip] 26. Char, String 기본 사용법 (1) | 2024.11.10 |
---|---|
[C/C++ Tip] 25. C언어 2차원 배열 (0) | 2024.11.10 |
[C/C++ Tip] 23. 지역 변수, 전역 변수, 정적 변수 (0) | 2024.11.09 |
[C/C++ Tip] 22. C언어 반복문 : While문 (0) | 2024.11.09 |
[C/C++ Tip] 21. C언어 반복문: For문 (0) | 2024.11.09 |