관리 메뉴

공부 기록장 💻

[C언어] 구조체를 이용한 소규모 데이터베이스 관리 프로그램 본문

# Language & Tools/C

[C언어] 구조체를 이용한 소규모 데이터베이스 관리 프로그램

dream_for 2021. 2. 3. 19:11

 

구조체를 이용하여 데이터베이스의 기능을 하는 간단한 프로그램을 만들어 보자.

(곡 명, 가수, 장르의 데이터를 담은 음악 파일을 추가, 출력, 검색, 삭제할 수 있는 프로그램)

 

 

필요한 함수와 자료들 하나씩 살펴보자.

 

 

- <string.h> 헤더 파일: strcmp() 문자열 처리 라이브러리 함수

 

- music 구조체와 구조체 배열: 곡 명(문자열), 가수(문자열), 장르(정수형)

typedef struct music {
	char title[20];
	char artist[20];
	int genre;
}MUSIC;

int CNT = 0;
MUSIC mlist[100] = { 0, };

곡 명과 가수 이름은 문자형으로, 장르를 나타내는 변수는 정수형으로 멤버들을 선언하여 구조체를 정의한다.

가독성을 높이기 위해 typedef를 이용해 MUSIC 이라는 새로운 자료형으로 정의하였다.

구조체 배열 mlist을 선언해준다. 충분한 메모리 공간을 확보해두고, null 값으로 초기화한다.

배열 요소들에 접근하는 시점에 배열에 저장되어 있는 요소의 개수를 알아야 하므로, 전역 변수를 선언해준다. 0으로 초기화한다.

 

- 열거형: music 구조체 변수의 장르 타입 / 선택한 메뉴 종류

enum genre {KPOP, POP, CLASSIC, OST};
enum choice { ADD=1, PRINT, SEARCH, DELETE};

각 곡의 장르를 정수형 변수로써 선언하였기 때문에 열거형을 사용하여 각 장르를 의미하는 정수를 문자열 상수로써 정의하였다.

메뉴를 입력 받아 코드를 실행하는 경우, 가독성을 높이기 위해 이를 열거형으로 선언하였다.

 

- main() 함수 : 메뉴를 입력받아 실행시키고자 하는 기능을 하는 함수를 호출

int main(void) {
	int choice;

	menu();
	while (1) {
		printf("\n메뉴 입력: ");
		scanf_s("%d", &choice);
		if (choice == 5)break;
		getchar();

		switch (choice) {
		case ADD:
			mlist[CNT] = add_record();
			CNT++;
			break;
		case PRINT:
			print_record(mlist, CNT);
			break;
		case SEARCH:
			search_record(mlist, CNT);
			break;
		case DELETE:
			delete_record(mlist, CNT);
			break;
		default:break;
		}
	}
	return 0;
}

사용자로부터 메뉴를 여러 번 입력 받을 수 있도록, 반복문을 사용하였다.

scanf_s() 함수로 입력받은 후에 줄바꿈 문자가 버퍼에 저장되므로, 이를 제거하기 위해 getchar() 함수를 호출하도록 했다.

입력받는 정수에 따라 함수가 실행된다.

add_record() 함수를 실행한 뒤에 요소의 개수 CNT를 하나 증가시키도록 한다.

delete_record() 함수가 실행될 때는 목록에서 입력받은 곡 명을 찾지 못하는 경우가 발생할 수도 있기 때문에 해당 함수 내에서 성공적으로 삭제가 된 후에 CNT를 감소하도록 하였다.

 

 

- menu() 함수 : 메뉴 출력

void menu() {
	printf("=====================\n");
	printf(" 1. 추가\n 2. 출력\n 3. 검색 \n 4. 삭제\n 5. 종료\n");
	printf("=====================\n");
}

 

- add_record() 함수 : 구조체 배열에 새로운 데이터를 추가 - 멤버 변수들의 값을 입력 받아 생성한 구조체를 반환

MUSIC add_record() {
	MUSIC m;

	printf("제목: ");
	gets(m.title, 20);
	printf("가수: ");
	gets(m.artist, 20);
	printf("장르(0: 가요, 1: 팝, 2: 클래식, 3: 영화음악) : ");
	scanf_s("%d", &m.genre);
	getchar();

	return m;
}

구조체의 변수를 선언하고 곡의 제목, 가수, 장르를 입력받아 구조체의 멤버에 각 값을 저장하도록 한다.

이후 생성된 구조체를 반환하도록 한다.

 

- print_record() 함수 : 구조체 배열의 모든 요소를 출력 - 구조체 배열과 요소 개수를 인수로 전달 받아 전체 요소를 출력

void print_record(MUSIC* list, int cnt) {
	for (int i = 0;i < cnt;i++) {
		printf("< %d번째 곡 정보 >\n", i);
		printf("제목: %s\n", list[i].title);
		printf("가수: %s\n", list[i].artist);
		switch (list[i].genre) {
		case KPOP:printf("장르: 가요\n\n");break;
		case POP:printf("장르: 팝\n\n");break;
		case CLASSIC:printf("장르: 클래식\n\n");break;
		case OST:printf("장르: 영화음악\n\n");break;
		}
	}
}

구조체 포인터(=배열의 시작주소)와 배열 요소 개수를 인수로 전달 받는다.

요소의 개수만큼, 저장되어 있는 곡 정보를 출력하도록 한다.

장르는 곡마다 다르기 때문에, switch-case 문을 이용하여 출력하도록 했다.

 

 

- search_record() 함수 : 구조체 배열의 요소를 검색하여 출력 - 구조체 배열과 요소 개수를 인수로 전달 받아 입력 받은 문자열과 일치하는 곡 제목이 배열에 존재하는 경우 곡에 대한 정보 출력

void search_record(MUSIC* list, int cnt) {
	char title[20];
	printf("검색할 곡 제목: ");
	gets(title, 20);
	
	for (int i = 0;i < cnt;i++) {
		if (strcmp(title, list[i].title) == 0) {
			printf("%d번째 목록에서 %s를 찾음\n", i, title);
			printf("가수: %s\n", list[i].artist);
			switch (list[i].genre){
				case KPOP:printf("장르: 가요\n\n");break;
				case POP:printf("장르: 팝\n\n");break;
				case CLASSIC:printf("장르: 클래식\n\n");break;
				case OST:printf("장르: 영화음악\n\n");break;
				default:break;
			}
			return 0;
		}
	}
	printf("목록에서 %s 검색 실패\n\n", title);
}

문자열을 사용자로부터 입력받는다.

strcmp() 문자열 처리 라이브러리 함수를 이용하여 배열에 저장되어 있는 요소들의 곡 제목과 문자열을 비교하여,

동일한 것을 찾은 경우 해당 곡의 정보를 출력하고 함수를 종료시킨다.

모든 배열 요소를 검사한 후에 반복문을 빠져나가면, 문자열과 동일한 곡 명을 찾지 못했다는 뜻이므로 검색 실패했다는 문장을 출력하도록 한다.

 

 

- delete_record() 함수 : 구조체 배열의 요소 삭제 - 구조체 배열과 요소 개수를 인수로 전달 받아 입력 받은 문자열과 일치하는 곡 제목이 배열에 존재하는 경우 해당 곡 삭제

void delete_record(MUSIC* list, int cnt) {
	char title[20];
	int i;

	printf("삭제할 곡 제목: ");
	gets(title, 20);

	for (i = 0;i < cnt;i++) {
		if (strcmp(title, list[i].title) == 0) {
			printf("%d번째 목록에서 %s를 찾음\n", i, title);
			break;
		}
	}
	if (i == cnt) {
		printf("%s를 목록에서 찾지 못함\n\n", title);
		return 0;
	}
	for (int j = i;j < cnt - 1;j++) {
		list[j] = list[j + 1];
	}
	CNT--;
	printf("%s를 성공적으로 삭제함\n", title);
}

search_record() 함수와 동일한 방법으로 곡을 찾는다.

for문의 제어 변수 i가 cnt 값에 도달한 경우엔 바로 문장을 출력한 뒤 해당 함수로부터 빠져나가게 한다.

 

그렇지 않은 경우에는, 해당 곡의 정보를 목록으로부터 제거해야 한다.

구조체는 대입이 가능하므로, 마지막 i의 값(=삭제해야 할 배열 요소)에 다음 구조체를 대입하도록 한다.

(cnt - 1) 까지 위와 같은 동작을 실행하도록 하고, CNT 값을 하나 감소시킨다.

 

 

 

전체 코드를 모아보면, 다음과 같다.

 

#include <stdio.h>
#include <string.h>

enum genre{KPOP, POP, CLASSIC, OST};
enum choice { ADD=1, PRINT, SEARCH, DELETE};

typedef struct music {
	char title[20];
	char artist[20];
	int genre;
}MUSIC;

int CNT = 0;
MUSIC mlist[100] = { 0, };

void menu();
MUSIC add_record();
void print_record(MUSIC* list, int cnt);
void search_record(MUSIC* list, int cnt);
void delete_record(MUSIC* list, int cnt);

int main(void) {
	int choice;

	menu();
	while (1) {
		printf("\n메뉴 입력: ");
		scanf_s("%d", &choice);
		if (choice == 5)break;
		getchar();

		switch (choice) {
		case ADD:
			mlist[CNT] = add_record();
			CNT++;
			break;
		case PRINT:
			print_record(mlist, CNT);
			break;
		case SEARCH:
			search_record(mlist, CNT);
			break;
		case DELETE:
			delete_record(mlist, CNT);
			break;
		default:break;
		}
	}
	return 0;
}

void menu() {
	printf("=====================\n");
	printf(" 1. 추가\n 2. 출력\n 3. 검색 \n 4. 삭제\n 5. 종료\n");
	printf("=====================\n");
}

MUSIC add_record() {
	MUSIC m;

	printf("제목: ");
	gets(m.title, 20);
	printf("가수: ");
	gets(m.artist, 20);
	printf("장르(0: 가요, 1: 팝, 2: 클래식, 3: 영화음악) : ");
	scanf_s("%d", &m.genre);
	getchar();

	return m;
}

void print_record(MUSIC* list, int cnt) {
	for (int i = 0;i < cnt;i++) {
		printf("< %d번째 곡 정보 >\n", i);
		printf("제목: %s\n", list[i].title);
		printf("가수: %s\n", list[i].artist);
		switch (list[i].genre) {
		case KPOP:printf("장르: 가요\n\n");break;
		case POP:printf("장르: 팝\n\n");break;
		case CLASSIC:printf("장르: 클래식\n\n");break;
		case OST:printf("장르: 영화음악\n\n");break;
		}
	}
}

void search_record(MUSIC* list, int cnt) {
	char title[20];
	printf("검색할 곡 제목: ");
	gets(title, 20);
	
	for (int i = 0;i < cnt;i++) {
		if (strcmp(title, list[i].title) == 0) {
			printf("%d번째 목록에서 %s를 찾음\n", i, title);
			printf("가수: %s\n", list[i].artist);
			switch (list[i].genre){
				case KPOP:printf("장르: 가요\n\n");break;
				case POP:printf("장르: 팝\n\n");break;
				case CLASSIC:printf("장르: 클래식\n\n");break;
				case OST:printf("장르: 영화음악\n\n");break;
				default:break;
			}
			return 0;
		}
	}
	printf("목록에서 %s 검색 실패\n\n", title);
}

void delete_record(MUSIC* list, int cnt) {
	char title[20];
	int i;

	printf("삭제할 곡 제목: ");
	gets(title, 20);

	for (i = 0;i < cnt;i++) {
		if (strcmp(title, list[i].title) == 0) {
			printf("%d번째 목록에서 %s를 찾음\n", i, title);
			break;
		}
	}
	if (i == cnt) {
		printf("%s를 목록에서 찾지 못함\n\n", title);
		return 0;
	}
	for (int j = i;j < cnt - 1;j++) {
		list[j] = list[j + 1];
	}
	CNT--;
	printf("%s를 성공적으로 삭제함\n", title);
}

 

 

아래 실행 결과 보기

더보기

 

프로그램 실행 결과를 통해 확인해 보아야 할 것

 

- add_record() 함수 실행하여 구조체 배열에 요소가 추가 되었는지, 배열 요소의 개수가 하나 증가 하였는지

- print_record() 함수 실행하여 모든 구조체 배열 요소들이 출력 되는지

- search_record() 함수 실행하여 입력받은 문자열과 목록에 있는 배열 요소의 데이터와 비교하여 탐색을 성공한 경우 올바르게 출력하는지, 실패한 경우 검색 실패 코드 출력하는지

- delete_record() 함수 실행하여 입력받은 문자열과 목록에 있는 배열 요소의 데이터와 비교하여 탐색을 성공한 경우 해당 요소를 제거하고 배열 요소의 개수가 하나 감소 하였는지, 탐색에 실패한 경우 검색 실패 코드 출력하는지

 

배열 요소를 출력하는 함수를 잘 활용하여 확인하면 됨

 

 

 

 

728x90
반응형
Comments