2020. 4. 26.

[자료구조] 인프런 10. 전화번호부 v4.0 강의 정리


많은 항목 추가하기, 구조체(structure)



사람에 대해서 이름, 전화번호, 이메일 주소, 그리고 그룹을 지정할 있다. 이름을 제외한 다른 항목들은 비워둘 수도 있다.



한줄에 한명씩저장

이름 # 번호 # 이메일 # 그룹

*구조체

항상 가팅 붙어다녀야 하는 데이터를 별개의 변수들에 분산해서 저장하는 것은 바람직하지 않다.
어떤 사람의 이름, 전화번호, 이메일 주소 등이 그런 예이다.
c언어에서는 이런 경우 구조체(structure) 사용한다.


*자료구조: 구조체


#pragma warning(disable:4996)
#include
#include
#include
#define CAPACITY 100
#define BUFFER_LENGTH 100

typedef struct person {
char* name;
char* number;
char* email;
char* group;
} Person;
//구조체 struct person을 정의하면서
// 동시에 그것을 Person으로 renaming했다.
// 이런식으로 사용할 경우 structure tag person을 생략해도 된다.

Person directory[CAPACITY];
// person 타입의 배열 directory를 선언한다.


// number of people in phone directory

int read_line(FILE* fp, char str[], int n);
void find(char* name);
void status();
void remove(char* name);
void printf_person(Person p);
int search(char* name);
void handle_add(char* name);
void add(char* name, char* number, char* email, char* group);
void save(char* fileName);
int compose_name(char str[], int limit);
void load(char* fileName);

int n = 0;

int main() {
char command_line[BUFFER_LENGTH];
char* command, * argument;
char name_str[BUFFER_LENGTH];

while (1)
{
printf(" $");
// 프롬프트 출력
if (read_line(stdin, command_line, BUFFER_LENGTH) <= 0)
//stdin 표준 키보드 입력 받기
// 아무것도 입력하지 않을경우 다시 반복문으로
continue;

command = strtok(command_line, " ");
// 공백문자를 기준으로 해서 문장을 단어로 짜른다.                

if (strcmp(command, "read") == 0)
{
argument = strtok(NULL, " ");
if (argument == NULL)
{
printf("Invalid arguments.\n");
continue;
}
load(argument);
}
else if (strcmp(command, "add") == 0)
{
if (compose_name(name_str, BUFFER_LENGTH) <= 0)
{
printf("Name required. \n");
continue;
}
handle_add(name_str);
}
else if (strcmp(command, "find") == 0)
{
if (compose_name(name_str, BUFFER_LENGTH) <= 0)
{
printf("Name required. \n");
continue;
}
find(name_str);
}
// compose_name은 나머지 토큰들을 merge하여 이름을 구성한다.
else if (strcmp(command, "status") == 0)
{
status();
}
else if (strcmp(command, "delete") == 0)
{
if (compose_name(name_str, BUFFER_LENGTH) <= 0)
{
printf("Invalid arguments. \n");
continue;
}
remove(name_str);
}
else if (strcmp(command, "save") == 0)
{
argument = strtok(NULL, " ");
if (strcmp(argument, "as") != 0)
{
printf("Invalid arguments \n");
continue;
}

argument = strtok(NULL, " ");
if (argument == NULL)
{
printf("Invalid arguments.\n");
continue;
}
save(argument);
}
else if (strcmp(command, "exit") == 0)
break;
}
return 0;
}

void load(char* fileName) {
char buffer[BUFFER_LENGTH];
char* name, * number, * email, * group;

FILE *fp = fopen(fileName, "r");
if (fp == NULL)
{
printf("Open failed.\n");
return;
}
while (1)
{
if (read_line(fp, buffer, BUFFER_LENGTH) <= 0)
break;
name = strtok(buffer, "#");
number = strtok(NULL, "#");
email = strtok(NULL, "#");
group = strtok(NULL, "#");
add(name, number, email, group);
}
fclose(fp);
}

int compose_name(char str[], int limit) {
char* ptr;
int length = 0;

ptr = strtok(NULL, " ");
if (ptr == NULL)
return 0;
// 빈값이라면 0을 리턴해주며 끝낸다.

strcpy(str, ptr);
length += strlen(ptr);
// 포인터의 길이를 더한다.

while ((ptr = strtok(NULL, " ")) != NULL)
{
if (length + strlen(ptr) + 1 < limit)
{
str[length++] = ' ';
// 공백문자를 쓴다.
str[length] = '\0';
// 널캐릭터를 추가해줘야한다.
strcat(str, ptr);
// \0로 끝난다는 가정하에 동작함
length += strlen(ptr);
}
}
return length;
}

//command_lien의 남아있는 토큰들을 모두 합쳐 이름을 나타내는 문자열을 구성한다.
//토큰과 토큰 사이에 하나의 공백문자를 삽입한다.

void save(char* fileName) {
int i;
FILE* fp = fopen(fileName, "w");
if (fp == NULL)
{
printf("Open failed.\n");
return;
}

for (i = 0; i < n; i++)
{
fprintf(fp, "%s#", directory[i].name);
fprintf(fp, "%s#", directory[i].number);
fprintf(fp, "%s#", directory[i].email);
fprintf(fp, "%s#\n", directory[i].group);
}
fclose(fp);
}

void add(char* name, char* number, char* email, char* group) {
int i = n - 1;
while (i >= 0 && strcmp(directory[i].name, name) > 0)
// 정렬 루프
{
directory[i + 1] = directory[i];
// 스트럭쳐사이에 취합문 사용가능
i--;
}
directory[i + 1].name = strdup(name);
directory[i + 1].number = strdup(number);
directory[i + 1].email = strdup(email);
directory[i + 1].group = strdup(group);
n++;
// 모든 항목들은 strdup로 복제하여 저장한다.
}

void handle_add(char* name)
{
char number[BUFFER_LENGTH], email[BUFFER_LENGTH], group[BUFFER_LENGTH];
char empty[] = " ";
printf("    Phone: ");
read_line(stdin, number, BUFFER_LENGTH);
printf("    Email: ");
read_line(stdin, email, BUFFER_LENGTH);
printf("    Group: ");
read_line(stdin, group, BUFFER_LENGTH);

add(name,
(char*)(strlen(number) > 0 ? number : empty),
(char*)(strlen(email) > 0 ? email : empty),
(char*)(strlen(group) > 0 ? group : empty));
// 왼쪽 조건이 안되면 오른쪽을
// 오른쪽 조건 안되면 왼쪽을 택해라
}
// 존재하지 않는 항목들을 하나의 공백문자로 구성된 문자열로 대체한다.
// add 호출 라인이 복잡해진것은 앞단에 펑션중 add와 통일하기 위한 것이다.

int search(char* name)
{
int i;
for (i = 0; i < n; i++)
{
if (strcmp(name, directory[i].name) == 0)
{
return i;
}
}
return -1;
}

void printf_person(Person p)
{
printf("%s:\n", p.name);
printf("    Phone: %s\n", p.number);
printf("    Email: %s\n", p.email);
printf("    Group: %s\n", p.group);
}

void remove(char* name) {
int i = search(name);
// returns - 1 if not exists
if (i == -1)
{
printf("No person named '%s' exitsts.\n", name);
return;
}

int j = i;
for (j; j < n - 1; j++)
{
directory[j] = directory[j + 1];
//구조체 변수간의 치환연산이 지원되므로 멤버항목들을 따로따로 치환할 필요가 없다.
}
n--;
printf("'%s' was deleted successfully. \n", name);
}

void status() {
int i;
for (i = 0; i < n; i++)
printf_person(directory[i]);
printf("Total %d persons. \n", n);
}

void find(char* name) {
int index = search(name);
// search를 한다.
if (index == 1)
// search의 결과가 1이면 사람이 없다고 출력한다.
printf("No person named '%s' exist. \n", name);
else
printf_person(directory[index]);
}


int read_line(FILE* fp, char str[], int n)
{
int ch, i = 0;

while ((ch = fgetc(fp)) != '\n' && ch != EOF)
// 엔터를 치고 파일에 끝에 도달하지 않았을떄까지 돈다
if (i < n)
str[i++] = ch;

str[i] = '\0';
return i;
}





댓글 없음:

댓글 쓰기