C 언어 | 고급 기능 | 바이너리 파일 - fwrite(), fread()
표준 C의 함수를 사용하여 텍스트가 아닌 순수 이진 수열의 데이터를 파일에 읽고 쓰는 방법을 설명한다.
순수한 데이터
fputs()
함수와 fgets()
함수는 텍스트 데이터의 입출력을 할 때에 편리했다. 또한 데이터의 변환을 하는 경우는 fprintf()
함수와 fscanf()
가 편리하다. 그러나 이들은 ASCII 코드를 다루는 경우에는 편리하지만, 순수한 숫자 데이터를 입출력시키고 싶은 경우는 적절하지 않다. 텍스트가 아닌 원시 이진 데이터를 처리하려면이 함수는 사용할 수 없다.
물론 방법의 하나로는 fprintf()
와 fscanf()
를 사용하여 텍스트 및 숫자의 변환을 채택할 수 있다. 저장할 때는 텍스트 데이터로 변환하여 로드할 때에 필요한 데이터는 숫자로 변환하는 것이다. 그러나 변환을 할 때 걸리는 시간 문제도 있고, 텍스트 데이터는 다른 소프트웨어를 사용하여 저장된 데이터를 볼 수 있거나 재작성 될 가능성이 있다. 응용 프로그램의 종류에 따라서는 이것이 선호되지 않는 경우도 있을 것이다.
이진 데이터이면 프로그램이 직접 수치로 읽어 올 수 있다. 예를 들어 구조체의 값을 출력하는 것을 생각해보자. 이것을 텍스트 데이터를 취급되면, 번거로운 변환 처리을 작성해야 한다. 그러나 바이너리 데이터이면 직접 읽고 쓸 수 있는 것이다.
바이너리 데이터를 쓰는데는 fwrite() 함수를 사용한다.
fwrite() 함수
size_t fwrite( const void*buffer, size_t size, size_t count, FILE *stream );*stream );
buffer에 기록하는 데이터를 저장하는 메모리에 대한 포인터를 size에는 바이트 단위로 데이터의 크기를 count에는 데이터의 개수를 지정한다. 예를 들어 buffer가 int 형의 배열에 대한 포인터를 가리키는 한다면 size에는 sizeof(int)를 지정하고, count에 쓰는 요소의 수를 지정한다. 마지막 인수 stream에는 쓰는 대상의 스트림을 지정한다.
buffer는 void*
의 포인터이므로 형식은 묻지 않는다. 함수는 일체의 변환 처리를 하지 않고 입출력한다. 함수가 성공하면 실제로 쓴 항목 수를 반환한다. count 인수로 지정한 값보다 낮은 값이 반환된 경우, 어떠한 에러가 발생하고 있을 가능성이 있다.
fwrite() 함수는 인수가 복잡한 것처럼 보이지만, 단순하게 말한다면 buffer에서 size 바이트 단위로 count 회수로 stream에 기록하는 것이다. 배열 등의 연속적인 데이터를 저장할 때 유용하다.
순수한 바이너리 데이터로 입출력을 할 경우는 fopen()
함수의 액세스 모드를 지정으로 이진 데이터임을 나타내는 “b"를 지정한다. 쓰기 모드 “w"를 같이하여 “wb"으로 표현할 수 있다. 텍스트 데이터로 fwrite()
함수로 쓴 경우, 일부 코드가 자동 변환되어 버리는 등의 문제가 발생할 수 있다.
코드1
#include <stdio.h>
typedef struct {
int left , top , right , bottom;
} RECT;
int main() {
FILE *file;
RECT rect;
printf("사각형의 네 모서리의 좌표를 나타내는 숫자를 입력하십시오. >");
scanf("%d %d %d %d" , &rect.left , &rect.top , &rect.right , &rect.bottom);
printf("left=%d, top=%d, right=%d, bottom=%d\n",
rect.left , rect.top , rect.right , rect.bottom);
file = fopen("rect.dat" , "wb");
if (file == NULL) {
printf("파일을 쓰기 모드로 열 수 없습니다.");
return 0;
}
fwrite(&rect , sizeof(RECT) , 1 , file);
fclose(file);
return 0;
}
코드1은 값의 입력을 요구되므로 4개의 숫자를 입력한다. 이 값은 사각형의 좌표로 RECT 구조체의 변수에 저장된다. 그 후, rect.dat라는 파일이 생성되고, fwrite() 함수를 사용하여 값을 저장한다. fwrite() 함수에는 저장하는 대상 버퍼에 rect 변수에 대한 포인터를 지정하고, 크기에 sizeof(RECT)을 지정한다. rect 변수는 배열이 아니기 때문에 count 인수에는 1을 지정한다.
생성된 rect.dat 파일은 텍스트 데이터가 아닌 바이너리 데이터이므로, 바이너리 편집기 등으로 편집할 수 있다. 32비트 환경이라면 16바이트의 파일이 생성되어 있을 것이다.
바이너리 데이터를 읽어들이는 fread() 함수를 사용한다.
fread() 함수
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
기본적으로 인수는 fwrite()
함수와 동일하다. buffer에는 읽어 들인 데이터를 저장할 버퍼의 포인터를 지정하고, size는 바이트 단위로 데이터의 크기를, count에는 데이터의 개수를, stream에는 로드되는 스트림을 각각 지정한다. buffer는 읽기에 충분한 메모리 공간이 확보되어 있지 않으면 안된다. 반환 값은 실제로 읽은 전체 항목 수를 반환한다. 이 수가 count보다 작은 경우는 에러가 발생했는지 파일의 맨 끝에 알린다.
코드2
#include <stdio.h>
typedef struct {
int left , top , right , bottom;
} RECT;
int main() {
RECT rect;
FILE *file = fopen("rect.dat" , "rb");
if (file == NULL) {
printf("파일을 읽기 모드로 열 수 없습니다.\n");
return 0;
}
fread(&rect , sizeof(RECT) , 1 , file);
fclose(file);
printf("left=%d, top=%d, right=%d, bottom = %d\n" ,
rect.left , rect.top , rect.right , rect.bottom);
return 0;
}
코드2는 코드1에서 생성된 rect.dat 파일을 읽어 들이기 위한 프로그램이다. fread() 함수를 사용하여 RECT 형 변수 rect에 rect.dat 파일에서 데이터를 로드하는 것을 확인할 수있을 것이다. 프로그램은 마지막으로 printf() 함수 rect 변수의 값을 표시한다.