ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 자료구조 제 7강 (구조체)
    자료구조 2022. 2. 17. 21:11

     

     

    기능

    • 이름이 하나 이상의 단어로 구성될 수 있다.
    • 단어 사이에 여러 개의 공백이 있을 경우 한 칸의 공백으로 저장된다.
    • 이름을 제외하고는 모든 항목을 입력할 필요는 없다.
    • 각 사람에 대해서 이름, 전화번호, 이메일 주소, 그룹을 지정할 수 있다.

    저장방식

    • '#' 문자를 필드들 간의 구분자로 사용한다.
    • 존재하지 않는 항목의 경우 하나의 공백문자로 표시한다.
    • 모든 라인은 반드시 구분자로 끝난다.
    • 한 줄에 한 명씩 저장한다.

    구조체

    • 항상 같이 붙어다녀야 하는 데이터를 별개의 변수들에 분산해서 저장하는 것은 바람직하지 않다.
    • C언어에서는 이런 경우 구조체를 사용한다.

    함수

    • fgetc(파일명) : 파일로부터 한 character를 읽음

     

     

    /*
    * 2022-02-17
    * phonebook04.c
    * 구조체
    */
    
    #define _CRT_SECURE_NO_WARNINGS
    
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #define CAPACITY 100
    #define BUFFER_LENGTH 100
    
    typedef struct person {		//구조체 struct person을 정의하면서 동시에 그것을 Person으로 renaming했다.
    	char* name;				//이런 식으로 사용할 경우 structure tag인 person을 생략해도 된다.
    	char* number;
    	char* email;
    	char* group;
    } Person;
    
    Person directory[CAPACITY];	//Person 타입의 배열 directory를 선언
    int n = 0;	//phone directory의 사람의 수
    
    /*함수 선언*/
    int read_line(FILE* fp, char str[], int n);
    void load(char* fileName);
    void add(char* name, char* number, char* email, char* group);
    int compose_name(char str[], int limit);
    void handle_add(char* name);
    void save(char* fileName);
    int search(char* name);
    void print_person(Person p);
    void remover(char* name);
    void status();
    void find(char* name);
    
    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, " ");							//파일명은 공백문자가 들어갈 수 없으므로 strtok으로 받음
    			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);
    		}
    		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;
    			}
    			remover(name_str);
    		}
    		else if (strcmp(command, "save") == 0) {
    			argument = strtok(NULL, " ");
    			if (strcmp(argument, "as") != 0) {		//두번째 토큰이 as여야 함
    				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;
    }
    
    int read_line(FILE* fp, char str[], int n)			//키보드만 아니라 파일부터도 읽을 수 있음
    {
    	int ch, i = 0;
    	while ((ch = fgetc(fp)) != '\n' && ch != EOF)	//getchar의 파일 버전이 fgetc, 파일로부터 읽을 때 파일의 맨 마지막 줄에는 \n이 없으므로 EOF인지 검사함
    		if (i < n)
    			str[i++] = ch;
    	str[i] = '\0';
    	return i;
    }
    
    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);
    }
    
    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);			//모든 항목들은 strdup로 복제하여 저장
    	directory[i + 1].number = _strdup(number);
    	directory[i + 1].email = _strdup(email);
    	directory[i + 1].group = _strdup(group);
    	n++;
    	printf("%s was added successfully.\n", directory[i+1].name);
    }
    
    int compose_name(char str[], int limit) {			//command_line의 남아있는 토큰들을 모두 합쳐 이름을 나타내는 문자열을 구성한다.
    	char* ptr;										//토큰과 토큰 사이에 하나의 공백문자를 삽입한다.
    	int length = 0;
    
    	ptr = strtok(NULL, " ");
    	if (ptr == NULL)			//사람 이름이 없다는 뜻이므로 0 반환
    		return 0;
    
    	strcpy(str, ptr);			//이름의 첫번째 단어를 str에 저장
    	length += strlen(ptr);		//이름의 첫번째 글자의 길이 저장
    
    	while ((ptr = strtok(NULL, " ")) != NULL) {		//strtok의 결과가 NULL이 될 때 까지 반복
    		if (length + strlen(ptr) + 1 < limit) {		//오버플로우 방지하기 위함
    			str[length++] = ' ';
    			str[length++] = '\0';		//NULL자리에 공백문자를 저장했기 때문에 다시 NULL을 저장함
    			strcat(str, ptr);
    			length += strlen(ptr);
    		}
    	}
    	return length;
    }
    
    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));
    }
    
    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);
    }
    
    int search(char* name) {
    	int i;
    	for (i = 0; i < n; i++) {
    		if (strcmp(name, directory[i].name) == 0) {
    				return i;
    		}
    	}
    	return -1;
    }
    
    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);
    }
    
    void remover(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);
    }
    
    void status() {
    	int i;
    	for (i = 0; i < n; i++)
    		print_person(directory[i]);
    	printf("Total %d persons.\n", n);
    }
    
    void find(char* name) {
    	int index = search(name);
    	if (index == -1)
    		printf("No person named '%s' exists.\n", name);
    	else
    		print_person(directory[index]);
    }

     

     

    stdin은 키보드를 의미

Designed by Tistory.