C언어의 기초 개념들이 들어 있는 프로젝트, 도서 관리 프로젝트이다. 최종적으로 이러한 것을 만들 것이다.
콜솔이라 다소 조잡해 보이지만, 중요한 것은 내용이다!! API는 다 까먹었다!!! (충!성!)
내가 직접 도서관을 만든다고 생각하면서 차근차근 해야 할 것들을 생각해 보자. 우선 도서관에 있는 책들을 전산에 쓸 수 있도록 문서화해야 한다. 이것이 1번, 책을 새로 추가하기이다. 책들의 정보를 하나하나 추가해 주면서 총 몇권이 있는지도 확인해 주어야 할 것이다.
다음, 도서관에는 책들이 많다. 그래서 검색을 할 수가 있어야 한다. 검색을 하는 옵션으로는
1. 책 이름으로 검색
2. 저자로 검색
3. 출팔사로 검색
등을 생각해 볼 수 있을 것이다.
검색을 했다면, 책을 빌리고 반납할 수 있는 기능이 있어야 할 것이다. 이것이 3, 4번 내용이다.
다음으로, 이건 파일 입출력의 개념을 익히기 위해서 추가된 항목들인데, 책들의 내용을 txt파일로 출력하고, 반대로 이미 출력된 txt파일에서 책 리스트들을 불러올 수 있는 기능들을 추가할 것이다. 이렇게 말이다.
실컷 책들이름, 저자, 출판사, 대여 가능 여부들을 작성하고 5번을 누르면 아래와 같이 txt파일로 작성이 되게 할 것이다.
그리고 이렇게 작성된 파일을 불러와서 다시 사용할 수 있게 하는 것이 목표이다.
6번으로 리스트를 불러오고 7번으로 출력을 한 상황이다. 우선 가장 기본이 되는 책의 구조를 잡아보자.
1 2 3 4 5 6 | typedef struct BOOK { char book_name[30]; char auth_name[30]; char publ_name[30]; int borrowed; }BOOK; | cs |
이렇게 구조체로 구조(?!!)를 잡아주었다. 보통 struct BOOK ㅇㅇㅇ 이렇게 쓰기 싫어서 typedef를 붙여주는 경우가 가 많다. 그런 다음, 이 책들의 배열인 도서관을 선언할 것이다.
1 2 3 4 5 6 7 8 9 | int user_choice; /* 유저가 선택한 메뉴 */ int num_total_book = 0; /* 현재 책의 수 */ BOOK *book_list; printf("도서관의 최대 보관 장서 수를 설정해주세요 : "); scanf_s("%d", &user_choice); book_list = (BOOK *)malloc(sizeof(BOOK) * user_choice); | cs |
이는 사용자가 초기에 최대 권수를 설정할 수 있도록 동적 프로그래밍을 사용하였다. 그럼 이제, 각 기능들에 해당하는 함수들을 선언할 것이다.
1 2 3 4 5 6 7 | char search_str(char * dic, char * word); int register_book(BOOK *book_list, int *nth); int search_book(BOOK *book_list, int total_num_book); int borrow_book(BOOK *book_list); int return_book(BOOK *book_list); int print_book_list(BOOK *book_list, int total_num_book); int retrieve_book_info(BOOK **book_list, int *total_num_book); | cs |
각 함수들은, 책을 새로 추가하기, 책을 검색하기, 책을 빌리기/반납하기, 책들의 내용을 출력, 책들의 내용을 불러오는 기능들을 하는 함수들이다. 하나씩 만들어 보자. 그리고 함수들을 만들면서 어떠한 값들을 input으로 받아야 할지 생각을 해야 한다. 기본적으로 BOOK들의 리스트를 받고, 반복을 얼마나 해야 할 지 알기 위해서 전체 책이 몇 권이 있는지를 받기로 하였다. 제일 쉬운 책 추가부터 해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int register_book(BOOK *book_list, int *nth) { printf("책의 이름 : "); scanf_s("%s", book_list[*nth].book_name, sizeof(book_list[*nth].book_name)); printf("책의 저자 : "); scanf_s("%s", book_list[*nth].auth_name, sizeof(book_list[*nth].auth_name)); printf("책의 출판사 : "); scanf_s("%s", book_list[*nth].publ_name, sizeof(book_list[*nth].publ_name)); book_list[*nth].borrowed = 0; (*nth)++; return 0; } | cs |
그냥 scanf로 모두 받은 다음, 전체 권수를 추가만 해 주면 될 것이다. 다음은, 책 검색.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | int search_book(BOOK *book_list, int total_num_book) { int user_input; /* 사용자의 입력을 받는다. */ int i; char user_search[30]; printf("어느 것으로 검색 할 것인가요? \n"); printf("1. 책 제목 검색 \n"); printf("2. 지은이 검색 \n"); printf("3. 출판사 검색 \n"); scanf_s("%d", &user_input); printf("검색할 단어를 입력해주세요 : "); scanf_s("%s", user_search, sizeof(user_search)); printf("검색 결과 \n"); if (user_input == 1) { for (i = 0; i < total_num_book; i++) { if (search_str(book_list[i].book_name, user_search) >= 0) { printf("번호 : %d // 책 이름 : %s // 지은이 : %s // 출판사 : %s \n", i, book_list[i].book_name, book_list[i].auth_name, book_list[i].publ_name); } else printf("그런 책 없음.\n"); } } else if (user_input == 2) { for (i = 0; i < total_num_book; i++) { if (search_str(book_list[i].auth_name, user_search) >= 0) { printf("번호 : %d // 책 이름 : %s // 지은이 : %s // 출판사 : %s \n", i, book_list[i].book_name, book_list[i].auth_name, book_list[i].publ_name); } else printf("그런 책 없음.\n"); } } else if (user_input == 3) { for (i = 0; i < total_num_book; i++) { if (search_str(book_list[i].publ_name, user_search) >= 0) { printf("번호 : %d // 책 이름 : %s // 지은이 : %s // 출판사 : %s \n", i, book_list[i].book_name, book_list[i].auth_name, book_list[i].publ_name); } else printf("그런 책 없음.\n"); } } return 0; } | cs |
어떤 정보로 검색을 할 지 결정하고, search_str로 실질적인 검색을 한다. search_str은 그냥 문자열을 검색하는 함수이고, 간단하게 그 문자열이 있는지만 보는 함수이다. 아래와 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | char search_str(char * dic, char * word) { int loc = 0; int search_loc = 0; while (*dic) { if (*dic == *word) { while (*word) { if (*dic != *word) { word -= search_loc; loc += search_loc; search_loc = 0; break; } dic++; word++; search_loc++; if (*word == 0) { return loc; } } } dic++; loc++; } return -1; } | cs |
책을 빌리고 반납하는 것은 매우 쉽다. borrowed 변수가 0이면 대출 가능한 것, 1이면 이미 대출된 것이라 하였다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | int borrow_book(BOOK *book_list) { /* 사용자로 부터 책번호를 받을 변수*/ int book_num; printf("빌릴 책의 번호를 말해주세요 \n"); printf("책 번호 : "); scanf_s("%d", &book_num); if (book_list[book_num].borrowed == 1) { printf("이미 대출된 책입니다! \n"); } else { printf("책이 성공적으로 대출되었습니다. \n"); book_list[book_num].borrowed = 1; } return 0; } int return_book(BOOK *book_list) { /* 반납할 책의 번호 */ int num_book; printf("반납할 책의 번호를 써주세요 \n"); printf("책 번호 : "); scanf_s("%d", &num_book); if (book_list[num_book].borrowed == 0) { printf("이미 반납되어 있는 상태입니다\n"); } else { book_list[num_book].borrowed = 0; printf("성공적으로 반납되었습니다\n"); } return 0; } | cs |
이제 책의 리스트를 파일 입출력을 이용해 메모장에 기록하는 함수를 만들 것이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | int print_book_list(BOOK *book_list, int total_num_book) { FILE *fp; errno_t err = fopen_s(&fp, "book_list.txt", "w"); if (err == 0) { printf("success!! "); } else { printf("error"); return 0; } fprintf(fp, "%d\n", total_num_book); for (int i = 0; i < total_num_book; i++) { fprintf(fp, "%s\n%s\n%s\n", book_list[i].book_name, book_list[i].auth_name, book_list[i].publ_name); if (book_list[i].borrowed == 0) fprintf(fp, "NO\n"); else fprintf(fp, "YES\n"); } fclose(fp); return 0; } | cs |
파일 입출력을 위해서 stdlib.h는 미리 include한 상태이다.
마지막으로 이미 만들어진 메모장 파일을 불러오는 기능을 만들었다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | int retrieve_book_info(BOOK **book_list, int *total_num_book) { FILE *fp; errno_t err = fopen_s(&fp, "book_list.txt", "r"); int total_book; char borrow[10]; if (err == 0) { printf("success!! "); } else { printf("error"); return 0; } fscanf_s(fp, "%d", &total_book); *total_num_book = total_book; free(*book_list); (*book_list) = (BOOK *)malloc(sizeof(BOOK) * total_book); ; for (int i = 0; i < total_book; i++) { fscanf_s(fp, "%s", (*book_list)[i].book_name, sizeof((*book_list)[i].book_name)); fscanf_s(fp, "%s", (*book_list)[i].auth_name, sizeof((*book_list)[i].auth_name)); fscanf_s(fp, "%s", (*book_list)[i].publ_name, sizeof((*book_list)[i].publ_name)); fscanf_s(fp, "%s", borrow, sizeof(borrow)); //printf("%s %s %s %s\n", (*book_list)[i].book_name, (*book_list)[i].auth_name, (*book_list)[i].publ_name, borrow); //printf("%d\n", strcmp(borrow, "NO")); if (strcmp(borrow, "NO") == 0) (*book_list)[i].borrowed = 0; else if(strcmp(borrow, "YES") == 0) (*book_list)[i].borrowed = 1; //printf("%d", (book_list[i])->borrowed); } return 0; } | cs |
여기에서 혼동이 올 수가 있는데 아래의 접근 방식이다.
(*book_list)[i].book_name
book_list는 BOOK들이 들어 있는 구조체 리스트이다. 그래서 아래와 같이 써 주면 book_list는 하나밖에 없는데 다음 book_list에 접근하려고 하기 때문에 작동이 되질 않는다. (참고로 이렇게 실수를 해도 어떠한 오류도 검출되지 않는다.) 아래의 풀이를 보면 이해가 될 것이다 .
(*book_list)[i].book_name == ( *( *book_list + i)).book_name
book_list[i]->book_name == ( *( *(book_list + i))).book_name
그럼, 전체 코드는 다음과 같아질 것이다. 아래 글자를 누르면 펼쳐진다.
아주 기초적인 프로그램이었다. 가끔 C를 까먹으면 다시 만들면서 복기할 생각이다.
'개발' 카테고리의 다른 글
C 언어 - 자주 사용하는 함수 read_line (0) | 2019.01.10 |
---|---|
C 언어 - strdup / _strdup (0) | 2019.01.10 |
C 언어 - 두고두고 쓰기 위한 문자열 search 함수 (0) | 2019.01.04 |
파이썬 2차원 배열 생성 (0) | 2019.01.02 |
C 언어 - 파일 입출력 (0) | 2018.12.31 |