[C 언어] - Phone Book V4

RuiN·2022년 8월 8일
0

Practice

목록 보기
4/4
post-thumbnail

V4 에서는 이름, 번호, 이메일, 그룹 을 추가하고. 추가양식을 나타내는 기능을 구현했다.


Main ( )

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

#define CAPACITY 100
#define BUFFER_LENGTH 100
#define _CRT_SECURE_NO_WARNINGS // 혹시 모를 fopen 오류 무시

typedef struct person {   // 이름 번호 이메일 그룹 struct
    char *name;
    char *number;
    char *email;
    char *group;
} Person;

Person directory[CAPACITY];     // directory person 배열

int read_line(FILE *fp, char str[], int n);            // 명령어 읽기
void load(char *fileName);       // 파일 로드
void add(char *name, char *number, char *email, char *group);   // 이름 번호 이메일 그룹 추가
void save(char *fileName);        // 파일 저장
void print_person(Person p);      // 출력
int search(char * name);          // 검색 -1 return
void remove_item(char*name);      // 삭제
void find(char*name);             // 이름으로 검색
void status();                    // 전체 조회
void handle_add(char *name);      // 데이터 생성 양식
int compose_name(char str[], int limit);    // 이름만 뺴오기
int n = 0;                // 인원 숫자

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

    while (1) {
        printf("$ ");
        if (read_line(stdin, command_line, BUFFER_LENGTH) <= 0) {
            continue;
        }

        command = strtok(command_line, " ");
        if (strcmp(command, "read") == 0) {
            arg = strtok(NULL, " ");
            if (arg == NULL) {
                printf("Invalid arguments.\n");
                continue;
            }
            load(arg);

        } 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);
        } else if (strcmp(command, "save") == 0) {
            arg = strtok(NULL, " ");
            if (strcmp(arg, "as") != 0) {
                printf("Invalid arguments.\n");
                continue;
            }
            arg = strtok(NULL, " ");
            if (arg == NULL) {
                printf("Invalid arguments.\n");
                continue;
            }
            save(arg);
        } 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_item(name_str);
        } else if (strcmp(command, "exit") == 0) {
            break;
        }
    }
    return 0;
}

모두 비슷합니다만..
전체 명령을 command_line에 입력받습니다. 그다음에 strtok 함수를 이용해서 단어들을 공백기준으로 나눈후,
진행합니다.


Read_Line ( )

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;
}
  • 위의 Main 함수에서 볼수 있듯이, stdin 을 사용하여 파일 뿐만아니라 키보드의 입력도 적용할 수 있게 했습니다.
  • 파일과, 파일의 내용을 담을 str 배열, 그리고 n ( Buffer size ) 을 받습니다.
  • V3 에서는 getchar( ) 를 사용했지만, V4 에서는 fgetc( ) 로 파일의 내용을 읽어옵니다.
    여기서도 또한 int를 return

Load ( )

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);
}
  • 파일을 읽어오는 로직, 파일이 없다면 open failed
  • 만약 파일의 내용이 없다면 break;
  • 파일의 한문장을 # 기준으로 나누고 add함수로 저장

Add ( )

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++;
}
  • 이름 번호 이메일 그룹을 인자로 받는 add 함수 생성
  • strcmp 함수로 저장되어있던 이름과 입력받은 이름을 비교하여 정렬하여 저장
  • 그리고 지역변수로 사라질 name, number, email, group을 directory배열내부의 각각 넣어줌

Handle_Add ( )

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)
        );
}
  • 입력받을 number와 email, group 배열 변수 선언
  • 만약 데이터가 없을경우의 넣어줄 emtpy선언
  • 순서대로 입력후, add 함수를 이용하여 추가
  • 삼항 조건식 ( null 일경우에 빈칸으로 입력 )

Compose Name ( )

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

    ptr = strtok(NULL, " ");
    if (ptr == NULL) {
        return 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);
            length += strlen(ptr);
        }
    }
    return length;
}

나름 이해하기 어려웠던 로직..

ptr 포인터 변수에 다음으로 입력받을 데이터를 공백으로 나눈다.
만약 입력된것이 없다면 return;

있다면, str에 ptr에 들어간 데이터를 복사한후, ptr의 길이만큼 legnth에 더합니다.
그다음에 ptr의 데이터가 strtok 으로 나눈후 의 데이터가 null 이 아닌동안에
길이가 limit을 넘지 않는다면, strtok 으로 나눈 문자열의 맨끝에 공백을,
문자열의 마지막은 \0으로 지어준다.

마지막으로 length 를 return


Find ( )

void find(char*name)
{
    int index = search(name);
    if (index == -1) {
        printf("No person named '%s' exists.\n", name);
    } else {
        print_person(directory[index]);
    }
}

Search ( )

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

find함수와 search 함수는 V3 에서 확인.


void print_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);
}

remove_item ( ) >> delete

void remove_item(char*name)
{
    int i = search(name);
    if (i == -1) {
        printf("No person named '%s' exists.\n", name);
        return;
    }
    int j =i;
    for (; j < n - 1; j++) {
        directory[j] = directory[j + 1];
    }
    n--;
    printf("'%s' was deleted successfully.\n", name);
}

Status ( )


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

Save ( )

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);
}

설명이 없는 부분은 이미 이전 포스트에서 다루었다.

profile
어디까지 올라갈지 궁금한 하루

0개의 댓글