C & C++/디딤돌 C언어

Part 32. 도서 관리 프로그램 IV - 저장 및 로드

언휴 2024. 1. 23. 13:55

Part 32. 도서 관리 프로그램 IV - 저장 및 로드

113. 도서 관리 프로그램 – 저장

C언어 도서 관리 프로그램 - 저장 기능 구현

이번에는 데이터를 파일에 저장하는 기능의 시퀀스 다이어그램을 작성하기로 해요.

C언어 도서 관리 프로그램 - 저장 기능 구현 시퀀스 다이어그램
저장 시퀀스 다이어그램

먼저 App에서는 파일을 쓰기 모드로 열어야겠죠.
그리고 App의 정보를 파일에 저장해요.
프로그램의 데이터를 다른 물리 매체에 선형으로 보내는 것을 직렬화라고 불러요.
직렬화 함수에서는 마지막 부여한 장르 번호와 배열에 보관한 장르 개수를 저장하세요

그리고 배열에 보관한 장르들의 정보를 파일에 직렬화하세요.
장르의 정보를 직렬화하는 함수에서도 장르번호, 장르명, 마지막 부여한 도서 번호를 파일에 저장하고 배열에 보관한 도서 개수를 저장하세요.

그리고 배열에 보관한 모든 장르를 파일에 직렬화하세요.
파일의 정보를 직렬화하는 함수에서는 자신의 정보를 저장하세요.
이와 같은 작업을 완료하면  App에서는 파일을 닫아야겠죠.

먼저 Book 헤더에 직렬화 함수를 선언하고 소스 파일에 함수를 구현하세요.
Book 개체의 모든 정보는 연속적인 메모리에 있으므로 fwrite 한 번만 호출하세요.

void BookSerialize(Book *book,FILE *fp)
{
    fwrite(book,sizeof(Book),1,fp);
}

Genre 헤더에도 직렬화 함수를 선언하고 소스 파일에 함수를 구현하세요.
Genre 개체 내부에는 도서를 보관하는 배열이 있어요.
따라서 Genre의 정보는 한 번에 저장할 수 없어요.
먼저 장르 이름, 장르 번호, 최근에 부여한 도서 번호를 저장하세요.
그리고 배열에 보관한 도서 개수를 얻어와서 파일에 저장하세요.
마지막으로 배열에 보관한 도서들의 정보를 직렬화하세요.

void GenreSerialize(Genre *genre,FILE *fp)
{
    Iterator seek;
    Iterator end;
    Book *book=0;
    int n = 0;

    fwrite(genre->name,sizeof(genre->name),1,fp);
    fwrite(&(genre->gnum),sizeof(int),1,fp);
    fwrite(&(genre->last_bnum),sizeof(int),1,fp);

    n = EHArrayGetSize(genre->books);
    fwrite(&n,sizeof(int),1,fp);
    seek= EHArrayBegin(genre->books);
    end= EHArrayEnd(genre->books);

    for(  ;seek != end; ++seek)
    {
        book = (Book *)(*seek);
        BookSerialize(book,fp);
    }
}

App에 Save 함수를 구현해 봐요.
먼저 파일을 쓰기 모드로 열어야겠죠.
그리고 정상적으로 열었다면 직렬화 함수를 호출하세요.

void AppSave(App *app)
{
    FILE *fp = 0;
    fopen_s(&fp, app->fname,"w");
    if(fp)
    {
        AppSerialize(app,fp);
        fclose(fp);
    }
    else
    {
        printf("오류!!!데이터 저장 실패\n");
    }
}

직렬화함수에서는 마지막 부여한 장르 번호와 배열에 보관한 장르 개수를 저장하세요.
그리고 반복해서 배열에 보관한 장르를 파일에 직렬화하세요.

void AppSerialize(App *app,FILE *fp)
{
    Iterator seek;
    Iterator end;
    int n = 0;
    Genre *genre=0;
    fwrite(&(app->last_gnum),sizeof(int),1,fp);
    n = EHArrayGetSize(app->genres);
    fwrite(&n,sizeof(int),1,fp);
    seek= EHArrayBegin(app->genres);
    end= EHArrayEnd(app->genres);
    for(  ;seek != end; ++seek)
    {
        genre = (Genre *)(*seek);
        GenreSerialize(genre,fp);
    }
}

114. 도서 관리 프로그램 – 로딩

C언어 도서 관리 프로그램 - 로딩 기능 구현

C언어 도서관리 프로그램 - 로딩 기능 구현 시퀀스 다이어그램
로딩 시퀀스 다이어그램

먼저 App에서는 파일을 읽기 모드로 열어야겠죠.
그리고 App의 정보를 파일에서 읽어오세요.
다른 물리 매체에 데이터를 선형으로 읽어와 프로그램 데이터가 되는 과정을 역직렬화라고 불러요.
역직렬화 함수에서는 마지막 부여한 장르 번호와 장르 개수를 로딩해야죠.
그리고 파일을 인자로 장르를 만들어 배열에 보관하세요.
파일로부터 장르를 만드는 함수에서는 역직렬화 과정이 필요해요.
장르번호, 장르명, 마지막 부여한 도서 번호를 파일에서 로딩하고 도서 개수를 로딩하세요.
그리고 파일에서 도서 개체를 만들어 배열에 보관하세요.
파일에서 도서 개체를 만드는 함수에서도 역직렬화를 수행하고 작업을 수행한 후에  App에서는 파일을 닫으세요.

먼저 파일에서 데이터를 로딩하여 Book 개체를 생성하는 파일을 헤더 파일에 선언하고 작성하세요.
먼저 NewBook 함수로 Book 개체를 생성하고 역직렬화 함수를 호출하고 Book 개체를 반환하세요.

void BookDeserialize(Book *book,FILE *fp);
Book *NewBook2(FILE *fp)
{
    Book *book = 0;
    book = NewBook(0,"","");
    BookDeserialize(book,fp);
    return book;
}

도서 역직렬화 함수에서는 파일에서 Book 형식 크기만큼 데이터를 파일에서 메모리로 읽어오세요.

void BookDeserialize(Book *book,FILE *fp)
{
    fread(book,sizeof(Book),1,fp);
}

파일에서 데이터를 로딩하여 Genre 개체를 생성하는 부분도 마찬가지죠.
장르 개체를 생성한 후에 역직렬화를 수행한 후에 생성한 Genre 개체를 반환하세요.

void GenreDeserialize(Genre *genre,FILE *fp);
Genre *NewGenre2(FILE *fp)
{
    Genre *genre = 0;
    genre = NewGenre(0,"");
    GenreDeserialize(genre,fp);
    return genre;
}

장르를 역직렬화하는 함수에서는 직렬화에서 저장한 순으로 로딩하세요.
장르 이름을 로딩하고 장르 번호와 마지막 부여한 도서 번호를 로딩하세요.
그리고 배열에 보관한 도서 개수를 로딩하세요.
그리고 반복문을 이용하여 파일에서 도서를 생성하는 함수를 호출한 후에 배열에 도서 개체를 보관하세요.

void GenreDeserialize(Genre *genre,FILE *fp)
{
    Book *book=0;
    int i = 0;
    int n = 0;
    fread(genre->name,sizeof(genre->name),1,fp);
    fread(&(genre->gnum),sizeof(int),1,fp);
    fread(&(genre->last_bnum),sizeof(int),1,fp);
    fread(&n,sizeof(int),1,fp);
    for(i=0  ;i<n; i++)
    {
        book = NewBook2(fp);
        EHArrayPushBack(genre->books,book);
    }
}

App의 Load 함수에서는 읽기 모드로 파일을 열어야겠죠.
정상적으로 열리면 역직렬화 함수를 호출하세요.

void AppLoad(App *app)
{
    FILE *fp = 0;
    fp = fopen(app->fname,"r");
    if(fp)
    {
        AppDeserialize(app,fp);
    }
    else
    {
        printf("환영합니다.\n");
    }
}

App의 역직렬화 함수도 직렬화 함수에서 수행한 순서대로 수행하세요.
마지막 부여한 장르 번호를 로딩하고 배열에 보관한 장르 개수를 로딩하세요.
그리고 반복문해서 파일로부터 장르 개체를 생성하여 배열에 보관하세요.

void AppDeserialize(App *app,FILE *fp)
{
    int n = 0;
    int i = 0;
    Genre *genre=0;
    fread(&(app->last_gnum),sizeof(int),1,fp);
    fread(&n,sizeof(int),1,fp);
    for( i=0 ;i<n;i++)
    {
        genre = NewGenre2(fp);
        EHArrayPushBack(app->genres,genre);
    }
}

 

마지막으로 진입점 main 함수에 argument 처리를 통해 데이터 파일을 선택하여 로딩 및 저장할 수 있게 합시다.

#include "App.h"
#define DEF_FNAME "member.ehd"
int main(int argc, char **argv)
{
	App* app = 0;
	if (argc != 2)
	{
		app = NewApp(DEF_FNAME);
	}
	else
	{
		app = NewApp(argv[1]);
	}
	AppRun(app);
	DeleteApp(app);
	return 0;
}

이상으로 장르별 도서 관리 프로그래밍을 마칠게요.
여러분께서는 시나리오를 다시 한번 보면서 이해하고 전체 개발 공정을 살펴본 후에 다시 한 번 작성해 보세요.
처음부터 스스로 작성할 수 있는 단계에 오면 여러분께서 시나리오를 만들어서 프로그래밍하면 보다 나은 학습이 될 것예요.