71.2 파일 크기만큼 파일 읽기
'70.4 파일에서 문자열 읽기'에서는 파일 크기와 상관없이 버퍼의 크기를 20바이트만큼 생성했습니다. 하지만 이렇게 되면 파일의 크기가 20바이트를 넘을 때는 내용을 모두 읽을 수 없게 됩니다. 이번에는 앞에서 배운 파일의 크기를 구하는 방법을 이용하여 파일 크기만큼 버퍼를 생성하고, 파일을 읽어보겠습니다.
read_entire_file.c
#define _CRT_SECURE_NO_WARNINGS // fopen 보안 경고로 인한 컴파일 에러 방지 #include <stdio.h> // fopen, fseek, ftell, fread, fclose 함수가 선언된 헤더 파일 #include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일 #include <string.h> // memset 함수가 선언된 헤더 파일 int main() { char *buffer; int size; int count; FILE *fp = fopen("hello.txt", "r"); // hello.txt 파일을 읽기 모드(r)로 열기. // 파일 포인터를 반환 fseek(fp, 0, SEEK_END); // 파일 포인터를 파일의 끝으로 이동시킴 size = ftell(fp); // 파일 포인터의 현재 위치를 얻음 buffer = malloc(size + 1); // 파일 크기 + 1바이트(문자열 마지막의 NULL)만큼 동적 메모리 할당 memset(buffer, 0, size + 1); // 파일 크기 + 1바이트만큼 메모리를 0으로 초기화 fseek(fp, 0, SEEK_SET); // 파일 포인터를 파일의 처음으로 이동시킴 count = fread(buffer, size, 1, fp); // hello.txt에서 파일 크기만큼 값을 읽음 printf("%s size: %d, count: %d\n", buffer, size, count); // Hello world! size: 13, count: 1: 파일의 내용, 파일 크기, 읽은 횟수 출력 fclose(fp); // 파일 포인터 닫기 free(buffer); // 동적 메모리 해제 return 0; }
실행 결과
Hello, world! size: 13, count: 1
소스 코드가 복잡해 보이지만 한 줄 한 줄 들여다보면 그렇게 어렵지 않습니다. 먼저 hello.txt 파일을 읽기 모드(r)로 열고, 파일의 크기를 구합니다.
FILE *fp = fopen("hello.txt", "r"); // hello.txt 파일을 읽기 모드(r)로 열기. // 파일 포인터를 반환 fseek(fp, 0, SEEK_END); // 파일 포인터를 파일의 끝으로 이동시킴 size = ftell(fp); // 파일 포인터의 현재 위치를 얻음
파일의 크기를 구했으니 버퍼를 생성합니다. 여기서 주의할 점은 파일의 문자열을 읽어서 C 언어 문자열로 만들 때는 문자열 마지막의 NULL 공간까지 확보해야 한다는 점입니다. 따라서 파일 크기 + 1바이트만큼 메모리를 할당합니다. 그리고 malloc 함수로 할당한 메모리는 이전에 사용하던 값이 지워지지 않고 남아있으므로 memset 함수를 사용하여 0으로 초기화해 줍니다.
buffer = malloc(size + 1); // 파일 크기 + 1바이트(문자열 마지막의 NULL)만큼 동적 메모리 할당 memset(buffer, 0, size + 1); // 파일 크기 + 1바이트만큼 메모리를 0으로 초기화
할당한 메모리를 NULL(0)으로 초기화하지 않으면 문자열 끝 부분에 다른 값이 들어있을 수도 있습니다. 이 상태에서는 문자열의 끝인 NULL을 찾을 수 없게 되어 문자열 이외의 값이 함께 출력되므로 주의해야 합니다.
앞에서 파일의 크기를 구할 때 fseek 함수로 파일 포인터를 파일의 끝으로 이동시켰습니다. 이 상태에서 fread 함수로 파일을 읽으면 파일의 끝 부분부터 읽게 되므로 아무 내용도 나오지 않습니다. 따라서 fseek 함수에 이동할 크기는 0, 기준점은 SEEK_SET으로 지정하여 파일 포인터를 파일의 맨 처음으로 이동시킨 뒤 fread 함수로 파일을 읽습니다.
fseek(fp, 0, SEEK_SET); // 파일 포인터를 파일의 처음으로 이동시킴 count = fread(buffer, size, 1, fp); // hello.txt에서 파일 크기만큼 값을 읽음
파일 포인터를 파일의 처음으로 이동시킬 때 fseek(fp, 0, SEEK_SET); 대신 rewind 함수를 사용해도 됩니다(stdio.h 헤더 파일에 선언되어 있습니다).
rewind(fp); // rewind 함수를 사용하여 파일 포인터를 파일의 처음으로 이동시킴 count = fread(buffer, size, 1, fp); // hello.txt에서 파일 크기만큼 값을 읽음
다음은 fseek 함수로 파일 포인터를 파일 끝, 파일 처음으로 이동시킨 그림입니다.
이제 printf로 buffer의 내용을 출력해보면 hello.txt의 내용인 "Hello,world!"가 출력됩니다.
printf("%s size: %d, count: %d\n", buffer, size, count); // Hello world! size: 13, count: 1: 파일의 내용, 파일 크기, 읽은 횟수 출력
파일 읽기가 끝났으면 파일 포인터도 닫고 버퍼에 할당된 동적 메모리도 해제합니다.
fclose(fp); // 파일 포인터 닫기 free(buffer); // 동적 메모리 해제