C언어에서 파일 입출력은 데이터 저장 및 관리를 위한 핵심적인 기능입니다. stdio.h
라이브러리를 활용하면 파일을 열고, 데이터를 읽거나 쓰며, 파일을 닫는 과정을 간단히 구현할 수 있습니다. 이 기사는 파일 입출력의 기본 개념과 주요 함수를 체계적으로 설명하여, 프로그래밍 초보자도 쉽게 이해할 수 있도록 돕습니다.
파일 입출력의 기본 개념
파일 입출력은 외부 저장소에 데이터를 읽거나 쓰는 과정을 의미합니다. C언어에서는 파일을 다루기 위해 FILE
포인터와 관련 함수를 사용합니다.
파일 입출력의 주요 단계
- 파일 열기: 파일을 열기 위해
fopen
함수를 사용하며, 파일을 읽기, 쓰기, 추가 등 다양한 모드로 열 수 있습니다. - 데이터 읽기/쓰기: 파일의 데이터를 읽거나 새 데이터를 파일에 기록합니다. 이를 위해
fgetc
,fgets
,fread
또는fputc
,fputs
,fwrite
함수를 사용합니다. - 파일 닫기: 파일 작업을 마친 후
fclose
함수를 호출하여 시스템 자원을 해제합니다.
파일 모드
fopen
함수는 파일을 열 때 다음과 같은 모드를 사용합니다:
"r"
: 읽기 전용 모드. 파일이 존재해야 함."w"
: 쓰기 전용 모드. 파일이 없으면 생성하고, 있으면 내용을 덮어씀."a"
: 추가 모드. 파일이 없으면 생성하며, 있으면 기존 내용 뒤에 덧붙임.
파일 입출력은 데이터 저장과 활용에 필수적인 도구이며, 올바른 파일 관리를 통해 안정적인 프로그램 작성을 가능하게 합니다.
`fopen`과 `fclose` 함수
`fopen` 함수
fopen
함수는 파일을 열고 작업을 시작하기 위한 첫 단계입니다. 이 함수는 파일을 열고 FILE
포인터를 반환합니다.
사용법
FILE *fopen(const char *filename, const char *mode);
- filename: 열고자 하는 파일의 이름(경로 포함).
- mode: 파일을 열 때 사용할 모드(읽기, 쓰기 등).
예시
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
printf("파일을 열 수 없습니다.\n");
}
`fclose` 함수
fclose
함수는 파일 작업을 완료한 후 시스템 자원을 해제하는 데 사용됩니다. 파일을 열었으면 반드시 닫아야 합니다.
사용법
int fclose(FILE *stream);
- stream: 닫을 파일의
FILE
포인터. - 반환값: 성공 시 0, 실패 시 EOF를 반환.
예시
if (fclose(file) != 0) {
printf("파일을 닫는 데 실패했습니다.\n");
}
중요 사항
- 파일 작업이 끝나면 반드시
fclose
를 호출하여 자원을 해제해야 메모리 누수를 방지할 수 있습니다. fopen
호출 후 반환값이NULL
인지 확인하여 파일 열기 실패를 처리해야 합니다.
이 두 함수를 올바르게 사용하는 것은 안전하고 효율적인 파일 입출력의 기초입니다.
파일 읽기 함수: `fgetc`, `fgets`, `fread`
`fgetc` 함수
fgetc
는 파일에서 한 문자씩 읽어옵니다. 주로 반복문과 함께 사용하여 파일의 모든 문자를 처리할 때 유용합니다.
사용법
int fgetc(FILE *stream);
- stream: 읽을 파일의
FILE
포인터. - 반환값: 읽은 문자(ASCII 코드). 파일의 끝에 도달하면 EOF를 반환.
예시
FILE *file = fopen("example.txt", "r");
if (file != NULL) {
char ch;
while ((ch = fgetc(file)) != EOF) {
putchar(ch);
}
fclose(file);
}
`fgets` 함수
fgets
는 파일에서 한 줄씩 읽어오는 데 사용됩니다. 줄 단위 처리가 필요한 경우 적합합니다.
사용법
char *fgets(char *str, int n, FILE *stream);
- str: 읽은 데이터를 저장할 버퍼.
- n: 읽을 최대 문자 수(종료 문자 포함).
- stream: 읽을 파일의
FILE
포인터.
예시
FILE *file = fopen("example.txt", "r");
if (file != NULL) {
char line[100];
while (fgets(line, sizeof(line), file) != NULL) {
printf("%s", line);
}
fclose(file);
}
`fread` 함수
fread
는 바이너리 데이터를 읽는 데 사용됩니다. 구조체나 큰 데이터 블록을 처리할 때 유용합니다.
사용법
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
- ptr: 읽은 데이터를 저장할 버퍼.
- size: 각 데이터 블록의 크기(바이트 단위).
- count: 읽을 데이터 블록의 개수.
- stream: 읽을 파일의
FILE
포인터.
예시
FILE *file = fopen("data.bin", "rb");
if (file != NULL) {
int buffer[10];
size_t itemsRead = fread(buffer, sizeof(int), 10, file);
printf("읽은 항목 수: %zu\n", itemsRead);
fclose(file);
}
차이점 요약
fgetc
: 파일에서 한 문자씩 읽음.fgets
: 파일에서 한 줄씩 읽음.fread
: 바이너리 데이터를 블록 단위로 읽음.
적절한 파일 읽기 함수를 선택하면 효율적인 파일 처리가 가능합니다.
파일 쓰기 함수: `fputc`, `fputs`, `fwrite`
`fputc` 함수
fputc
는 파일에 한 문자를 씁니다. 문자 단위로 파일에 데이터를 기록할 때 유용합니다.
사용법
int fputc(int ch, FILE *stream);
- ch: 파일에 쓸 문자(ASCII 코드).
- stream: 데이터를 기록할 파일의
FILE
포인터. - 반환값: 기록한 문자의 ASCII 코드, 실패 시 EOF 반환.
예시
FILE *file = fopen("example.txt", "w");
if (file != NULL) {
fputc('A', file);
fputc('B', file);
fclose(file);
}
`fputs` 함수
fputs
는 문자열 단위로 데이터를 기록합니다. 줄이나 문장을 파일에 쓸 때 적합합니다.
사용법
int fputs(const char *str, FILE *stream);
- str: 파일에 쓸 문자열.
- stream: 데이터를 기록할 파일의
FILE
포인터. - 반환값: 성공 시 0 이상, 실패 시 EOF 반환.
예시
FILE *file = fopen("example.txt", "w");
if (file != NULL) {
fputs("Hello, World!\n", file);
fputs("C programming is fun.", file);
fclose(file);
}
`fwrite` 함수
fwrite
는 바이너리 데이터를 블록 단위로 기록합니다. 구조체 또는 대량 데이터를 효율적으로 저장할 때 사용됩니다.
사용법
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
- ptr: 기록할 데이터가 저장된 버퍼.
- size: 각 데이터 블록의 크기(바이트 단위).
- count: 기록할 데이터 블록의 개수.
- stream: 데이터를 기록할 파일의
FILE
포인터. - 반환값: 성공적으로 기록한 블록의 개수.
예시
FILE *file = fopen("data.bin", "wb");
if (file != NULL) {
int buffer[] = {1, 2, 3, 4, 5};
size_t itemsWritten = fwrite(buffer, sizeof(int), 5, file);
printf("기록한 항목 수: %zu\n", itemsWritten);
fclose(file);
}
차이점 요약
fputc
: 한 문자를 파일에 기록.fputs
: 문자열을 파일에 기록.fwrite
: 바이너리 데이터를 블록 단위로 기록.
적절한 파일 쓰기 함수를 사용하면 효율적으로 데이터를 저장할 수 있습니다.
파일 포인터와 `ftell`, `fseek`, `rewind`
파일 포인터
파일 포인터는 파일 내 현재 읽기 또는 쓰기 위치를 가리키는 값입니다. 파일 작업 중에 특정 위치로 이동하거나 현재 위치를 확인할 때 유용합니다.
`ftell` 함수
ftell
은 현재 파일 포인터의 위치를 반환합니다. 파일 시작점으로부터 몇 바이트 떨어져 있는지 확인할 수 있습니다.
사용법
long ftell(FILE *stream);
- stream: 파일의
FILE
포인터. - 반환값: 파일의 시작점부터 현재 위치까지의 바이트 수, 실패 시 -1 반환.
예시
FILE *file = fopen("example.txt", "r");
if (file != NULL) {
fgetc(file); // 파일에서 한 문자 읽기
long position = ftell(file);
printf("현재 위치: %ld 바이트\n", position);
fclose(file);
}
`fseek` 함수
fseek
은 파일 포인터를 원하는 위치로 이동시킵니다. 파일 내에서 데이터를 무작위로 접근할 때 유용합니다.
사용법
int fseek(FILE *stream, long offset, int whence);
- stream: 파일의
FILE
포인터. - offset: 이동할 바이트 수.
- whence: 기준점(아래 참고).
SEEK_SET
: 파일의 시작점 기준.SEEK_CUR
: 현재 위치 기준.SEEK_END
: 파일의 끝 기준.- 반환값: 성공 시 0, 실패 시 -1.
예시
FILE *file = fopen("example.txt", "r");
if (file != NULL) {
fseek(file, 10, SEEK_SET); // 시작점으로부터 10바이트 이동
printf("새 위치: %ld 바이트\n", ftell(file));
fclose(file);
}
`rewind` 함수
rewind
는 파일 포인터를 파일의 시작점으로 즉시 이동시킵니다.
사용법
void rewind(FILE *stream);
- stream: 파일의
FILE
포인터.
예시
FILE *file = fopen("example.txt", "r");
if (file != NULL) {
fgetc(file); // 파일에서 한 문자 읽기
rewind(file); // 시작점으로 이동
printf("위치: %ld 바이트\n", ftell(file)); // 항상 0 출력
fclose(file);
}
차이점 요약
ftell
: 현재 파일 포인터 위치를 반환.fseek
: 파일 포인터를 지정된 위치로 이동.rewind
: 파일 포인터를 시작점으로 이동.
이 함수들은 파일 내 특정 위치로 효율적으로 이동하거나 데이터를 무작위로 접근하는 데 필수적입니다.
오류 처리와 `perror`, `feof` 함수
`perror` 함수
perror
는 파일 작업 중 발생한 오류를 간단히 출력합니다. 오류 원인을 명확히 파악할 때 유용합니다.
사용법
void perror(const char *message);
- message: 출력 메시지로, 오류 메시지와 함께 표시됨.
예시
FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
perror("파일 열기 오류");
}
출력 예시
파일 열기 오류: No such file or directory
`feof` 함수
feof
는 파일 끝(EOF)에 도달했는지 확인하는 데 사용됩니다. 반복문에서 파일 데이터를 읽는 도중 파일 끝을 정확히 확인할 수 있습니다.
사용법
int feof(FILE *stream);
- stream: 파일의
FILE
포인터. - 반환값: EOF에 도달하면 0이 아닌 값을 반환, 그렇지 않으면 0을 반환.
예시
FILE *file = fopen("example.txt", "r");
if (file != NULL) {
char ch;
while (!feof(file)) {
ch = fgetc(file);
if (ch != EOF) {
putchar(ch);
}
}
fclose(file);
}
추가적인 오류 처리
파일 작업 중 발생할 수 있는 일반적인 오류는 다음과 같습니다:
- 파일 없음:
fopen
에서 반환된 포인터가NULL
인지 확인. - 읽기/쓰기 실패: 반환값을 확인하여 적절히 처리.
- 잘못된 파일 포인터 사용: 닫힌 파일에 접근하지 않도록 주의.
예시 코드: 파일 오류 확인
FILE *file = fopen("data.txt", "r");
if (file == NULL) {
perror("파일 열기 실패");
return 1;
}
if (fgetc(file) == EOF && feof(file)) {
printf("파일이 비어 있습니다.\n");
}
fclose(file);
정리
perror
: 오류 메시지를 명확히 출력.feof
: 파일 끝에 도달했는지 확인.
효율적인 오류 처리는 파일 입출력의 안정성을 높이는 중요한 요소입니다.
응용 예시: 텍스트 파일 읽기 및 쓰기
예시 코드: 텍스트 파일 쓰기
아래 코드는 텍스트 파일에 데이터를 기록하는 간단한 예시입니다.
#include <stdio.h>
int main() {
FILE *file = fopen("output.txt", "w");
if (file == NULL) {
perror("파일 열기 실패");
return 1;
}
fputs("C 언어 파일 입출력 예제\n", file);
fputs("이 내용은 파일에 저장됩니다.\n", file);
fclose(file);
printf("파일 쓰기 완료\n");
return 0;
}
결과: output.txt
파일이 생성되며 다음 내용이 저장됩니다.
C 언어 파일 입출력 예제
이 내용은 파일에 저장됩니다.
예시 코드: 텍스트 파일 읽기
다음 코드는 텍스트 파일의 내용을 읽어 출력합니다.
#include <stdio.h>
int main() {
FILE *file = fopen("output.txt", "r");
if (file == NULL) {
perror("파일 열기 실패");
return 1;
}
char line[100];
while (fgets(line, sizeof(line), file) != NULL) {
printf("%s", line);
}
fclose(file);
return 0;
}
결과: output.txt
파일의 내용이 터미널에 출력됩니다.
예시 코드: 파일 복사
다음 코드는 텍스트 파일을 읽어 다른 파일에 복사합니다.
#include <stdio.h>
int main() {
FILE *src = fopen("output.txt", "r");
FILE *dest = fopen("copy.txt", "w");
if (src == NULL || dest == NULL) {
perror("파일 열기 실패");
return 1;
}
char ch;
while ((ch = fgetc(src)) != EOF) {
fputc(ch, dest);
}
fclose(src);
fclose(dest);
printf("파일 복사 완료\n");
return 0;
}
결과: copy.txt
파일이 생성되며 output.txt
의 내용이 복사됩니다.
요약
이 예제들을 통해 다음을 배울 수 있습니다:
- 파일 쓰기를 위한
fputs
사용. - 파일 읽기를 위한
fgets
사용. - 파일 데이터를 읽어 다른 파일에 저장하기.
실습을 통해 파일 입출력을 효과적으로 이해하고 응용할 수 있습니다.