#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#pragma pack(push,1)
//아카이브 헤더(아카이브 파일에 저장되는 구조체)
typedef struct _ARCHIVE_HEADER
{
uint16_t magic; //아카이브 파일 매직 넘버
uint16_t version; //아카이브 파일 버전
} ARCHIVE_HEADER, *PARCHIVE_HEADER;
//아카이브 파일 정보(아카이브 파일에 저장되는 구조체)
typedef struct _FILE_DESC
{
char name[256]; //파일 이름
uint32_t size; //파일 크기
uint32_t dataOffset; //파일 데이터 위치
} FILE_DESC, *PFILE_DESC;
#pragma pack(pop)
typedef struct _ARCHIVE //아카이브 메인 구조체
{
ARCHIVE_HEADER header; //아카이브 헤더
FILE *fp; //아카이브 파일 포인터
} ARCHIVE, *PARCHIVE;
#define ARCHIVE_NAME "archive.bin"
uint32_t getFileSize(FILE *fp) //파일의 크기를 구하는 함수 정의
{
uint32_t size;
uint32_t currPos = ftell(fp); //현재 파일 포인터의 위치를 저장함
//파일의 끝으로 가서 파일의 크기를 구함
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, currPos, SEEK_SET); //파일 포인터를 이전 위치로 되돌림
return size;
}
int append(PARCHIVE archive, char *filename) //파일 추가 함수 정의
{
int ret = 0; //함수의 성공 및 실패 여부를 반환값으로 표현
FILE *fp = fopen(filename, "rb");
if (fp == NULL)
{
printf("%s 파일이 없습니다.\n", filename);
return -1; //함수 종료, -1은 실패
}
uint8_t *buffer;
uint32_t size;
size = getFileSize(fp); //추가하는 파일의 크기를 구함
buffer = malloc(size);
if (fread(buffer, size, 1, fp) < 1) //추가할 파일의 내용을 읽음
{
printf("%s 파일 읽기 실패\n", filename);
ret = -1;
goto Error1;
}
PFILE_DESC desc = malloc(sizeof(FILE_DESC));
memset(desc, 0, sizeof(FILE_DESC));
strcpy(desc->name, filename); //파일 정보 구조체에 추가할 파일의 이름 저장
desc->size = size; //파일 정보 구조체에 추가할 파일의 크기 저장
fseek(archive->fp, sizeof(ARCHIVE_HEADER), SEEK_SET); //아카이브 헤더 바로 다음으로 파일 포인터를 이동시킴
desc->dataOffset = ftell(archive->fp) + sizeof(FILE_DESC);
//파일 데이터의 시작위치는 현재 파일 포인터의 위치에 파일 정보 크기만큼 순방향으로 이동시킨 값
if (fwrite(desc, sizeof(FILE_DESC), 1, archive->fp) < 1)
{
printf("파일 데이터 쓰기 실패\n");
ret = -1;
goto Error2;
}
printf("%s 파일 추가 성공\n크기: %d\n", filename, size);
Error2:
free(desc);
Error1:
free(buffer);
fclose(fp);
return ret;
}
int main()
{
PARCHIVE archive = malloc(sizeof(ARCHIVE));
memset(archive, 0, sizeof(ARCHIVE));
FILE *fp = fopen(ARCHIVE_NAME, "r+b"); //아카이브 파일을 읽기/쓰기 모드로 열음
if (fp == NULL) //아카이브 파일이 없으면
{
fp = fopen(ARCHIVE_NAME, "w+b"); //새로운 아카이브 파일을 생성함
if (fp == NULL) //파일 생성에 실패하면 프로그램 종료
return -1;
archive->header.magic = 'AF'; //매직넘버 'AF'저장(리틀 엔디언에서는 'FA'로 저장됨)
archive->header.version = 1; //파일 버전 1로 저장함
//아카이브 파일에 아카이브 헤더 저장
if (fwrite((&archive->header), sizeof(ARCHIVE_HEADER), 1, fp) < 1)
{
printf("아카이브 헤더 쓰기 실패\n");
fclose(fp);
return -1;
}
archive->fp = fp; //아카이브 파일 포인터 저장
append(archive, "hello.txt"); //hello.txt 파일 추가
}
fclose(fp); //아카이브 파일 포인터 닫기
free(archive); //동적 메모리 해제
return 0;
}
unit 82.4의 main.c 소스코드를 보고 위와 같이 작성했고 hello.txt 파일도 위 소스코드가 있는 폴더에 Hello, world!로 제대로 저장했습니다. 실행을 시켰더니 archive.bin 파일에 아래 사진처럼 파일 데이터 부분이 빠져있습니다. 왜 이런 건가요?...
캡처한 archive.bin 파일을 보면 hello.txt 파일 정보는 기록되었지만, hello.txt의 내용은 기록되지 않았습니다. 따라서 파일 데이터를 기록하는 부분을 봐야 합니다.
Unit 82.4의 예제에서 append 함수에는 다음 부분이 있습니다.
// 파일 데이터의 시작 위치는 현재 파일 포인터의 위치에 // 파일 정보 크기만큼 순방향으로 이동시킨 값 desc->dataOffset = ftell(archive->fp) + sizeof(FILE_DESC); // 아카이브 파일에 새 파일 정보 쓰기 if (fwrite(desc, sizeof(FILE_DESC), 1, archive->fp) < 1) { printf("파일 정보 쓰기 실패\n"); ret = -1; goto Error2; // fp를 닫고, desc와 buffer를 해제하는 에러 처리로 이동 } // 아카이브 파일에 새 파일 데이터 쓰기 if (fwrite(buffer, size, 1, archive->fp) < 1) { printf("파일 데이터 쓰기 실패\n"); ret = -1; goto Error2; // fp를 닫고, desc와 buffer를 해제하는 에러 처리로 이동 } printf("%s 파일 추가 성공\n크기: %d\n", filename, size);강조된 부분의 코드가 누락되어 있습니다.
작성한 예제 코드는 다음과 같습니다.
if (fwrite(desc, sizeof(FILE_DESC), 1, archive->fp) < 1)
{
printf("파일 데이터 쓰기 실패\n");
ret = -1;
goto Error2;
}
fwrite에서 파일 정보를 기록할 때는 sizeof(FILE_DESC) 크기만큼 기록하고, 파일 데이터를 기록할 때는 해당 파일의 size 크기만큼 기록합니다.
sizeof(FILE_DESC)를 기록할 때는 printf의 에러 메시지도 "파일 정보 쓰기 실패"로 바꿔야 합니다.
fwrite는 총 2번 호출됩니다. 1번은 파일 정보, 1번은 파일 내용입니다. 문의한 코드는 파일 정보는 기록하지만, 파일 내용은 기록하지 않았습니다.
예제에서 코드가 비슷한 부분이니 주의해서 비교해주세요.