2020. 4. 26.

[자료구조] 인프런 9. 전화번호부 v3.0(3) 강의 정리


완성본 눈알이 빠지는것같다
내손은 똥손이다 오타겁나낸다.


#pragma warning(disable:4996)
#include
#include
#include

#define INIT_CAPACITY 3
// 배열 재할당을 테스트하기 위해서 일부러 아주 작은 값으로
#define BUFFER_SIZE 50

char** names;
char** numbers;
// char * 타입의 배열의 이름으로 char**타입의 변수이다.
// 동적메모리 할당으로 크기를 키우려면 포인터로 선언해야한다.

int capacity = INIT_CAPACITY;
//size of arrays;
int n = 0;
// number of people in phone directory

// function prototypes here
void init_dierctory();
void process_command();
int read_line(char str[], int limit);
void load(char* fileName);
void add(char* name, char* number);
void status();
void find(char* name);
int search(char* name);
void remove(char* name);
void save(char* fileName);
void reallocate();

char delim[] = " ";


int main() {
init_dierctory();
// 이 함수에서 배열 name numbers를 생성한다.
process_command();
// 사용자의 명령을 받아 처리하는 부분을 별개의 함수로 만들었다.

return 0;
}

void init_dierctory() {
names = (char**)malloc(INIT_CAPACITY * sizeof(char*));
numbers = (char**)malloc(INIT_CAPACITY * sizeof(char*));
// 할당할 메모리의 byte수를 지정한다. 직접 숫자로 지정하는 것보다
// 이렇게 sizeof 연산자를 사용하는 것이 바람직하다.
// 배열의 크기가 INIT_CAPACITY이다.
}

int read_line(char str[], int limit)
// limit 보다 더 긴 line의 경우 뒷부분이 짤린다.
//
{
int ch, i = 0;

while ((ch = getchar()) != '\n')
// 줄바꿈 문자가 나올 때까지 읽는다.
// getchar 문자로 입력을 받지만 정수형으로 반환한다.
if (i < limit - 1)
// 배열의 용량을 초과하지 않을 때만 저장한다.
// limit - 1 을 한것은 \0가 들어가야 하는 공간
str[i++] = ch;

str[i] = '\0';
// 마지막에 null character ('\0')를 추가한다.

return i;
// 실제로 읽은 문자수를 반환한다.
}

void process_command() {
char command_line[BUFFER_SIZE];
// 한라인 통째로 읽어오기 위한 버퍼
char* command, * argument1, * argument2;

while (1)
{
printf("$ ");
if (read_line(command_line, BUFFER_SIZE) <= 0)
//명령줄을 통째로 읽는다.
continue;
command = strtok(command_line, delim);
if (command == NULL) continue;
// 첫번째 토큰은 명령어이다.

if (strcmp(command, "read") == 0)
// read 명령어라면
{
argument1 = strtok(NULL, delim);
//read명령에서 두번째 토큰은 파일명이다.
if (argument1 == NULL)
{
printf("File name required.\n");
continue;
}
load(argument1);
//파일명을 인자로 주면서 load를 호출한다.
}
else if (strcmp(command, "add") == 0)
{
argument1 = strtok(NULL, delim);
argument2 = strtok(NULL, delim);
// 명렁어에 이어지는 2개의 토큰은 각각 이름과 전화번호이다.
if (argument1 == NULL || argument2 == NULL)
{
printf("Invalid arguments. \n");
continue;
}
add(argument1, argument2);
// 이름과 전화번호를 인자로 주면서 add를 호출한다.
printf("%s was added successfully.\n", argument1);
}
else if (strcmp(command, "find") == 0)
{
argument1 = strtok(NULL, delim);
if (argument1 == NULL)
{
printf("Invalid arguments.\n");
continue;
}
find(argument1);
}
else if (strcmp(command, "status") == 0)
status();
else if (strcmp(command, "delete") == 0)
{
argument1 = strtok(NULL, delim);
if (argument1 == NULL) {
printf("Invalid argumnets. \n");
continue;
}
remove(argument1);
}
else if (strcmp(command, "save") == 0)
{
argument1 = strtok(NULL, delim);
argument2 = strtok(NULL, delim);
if (argument1 == NULL || strcmp("as", argument1) != 0
|| argument2 == NULL)
{
printf("Invalid command format.\n");
continue;
}
save(argument2);
}
else if (strcmp(command, "exit") == 0)
break;
}

}


void load(char* fileName) {
// 파일 이름을 매개변수로 제공되면 이파일로부터 데이터를 읽는다.
char buf1[BUFFER_SIZE];
char buf2[BUFFER_SIZE];

FILE* fp = fopen(fileName, "r");
if (fp == NULL)
//  파일 읽기에 실패했을경우 출력
{
printf("Open failed.\n");
return;
}
while ((fscanf(fp, "%s", buf1) != EOF))
{
fscanf(fp, "%s", buf2);
add(buf1, buf2);
}
fclose(fp);
}

void add(char* name, char* number) {
//사람이름과 전화번호를 매개변수로 준다.
if (n > capacity)
// n은 현재 저장된 사람이고
// capacity는 저장될 사람 수
reallocate();
// 배열이 꽉찬 경우 재할당한다.

int i = n - 1;
while (i >= 0 && strcmp(names[i], name) > 0)
// 알파벳순으로 판단한다.
{
names[i + 1] = names[i];
numbers[i + 1] = numbers[i];
i--;
}

names[i + 1] = strdup(name);
numbers[i + 1] = strdup(number);
// strdup이 반드시 필요한 이유는?

n++;
}

void status() {
int i;
for (i = 0; i < n; i++)
printf("%s %s\n", names[i], numbers[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("%s\n", numbers[index]);
}

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

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

int j = i;
for (j; j < n - 1; j++)
{
names[j] = names[j + 1];
numbers[j] = numbers[j + 1];
}
n--;
printf("'%s' was deleted successfully. \n", name);
}

void save(char * fileName) {
int i;
FILE* fp = fopen(fileName, "w");
// 파일에 쓸 때는 모드를 'w'로 하고 열어야 한다.
if (fp == NULL) {
printf("Open failed.\n");
return;
}
for (i = 0; i < n; i++) {
fprintf(fp, "%s %s\n", names[i], numbers[i]);
}
fclose(fp);
}

void reallocate()
{
int i;
capacity *= 2;
char** tmp1 = (char**)malloc(capacity * sizeof(char*));
char** tmp2 = (char**)malloc(capacity * sizeof(char*));
// 먼저 크기가 2배인 배열들을 할당한다.

// 원본 배열 names numbers의 값을 새로운 배열에 모두 복사한다.
for (i = 0; i < n; i++)
{
tmp1[i] = names[i];
tmp2[i] = numbers[i];
}

// 원본 배열 names numbers는 더 이상 필요없다.
//하지만 두 배열은 init_directory() 함수에서 동적메모리할당으로 만들어진 배열이므로
//그냥 두면 없어지지 않고 계속 존재한다. 이런 메모리를 garbage라고 부른다.
// garbage free함수를 이용하여 반환한ㄷ.
free(names);
free(numbers);

// names numbers가 새로운 배열을 가리키도록 한다.
//(배열의 이름은 포인터 변수이다.)        
names = tmp1;
numbers = tmp2;





}



댓글 없음:

댓글 쓰기