-
자료구조 제 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은 키보드를 의미
'자료구조' 카테고리의 다른 글
자료구조 제 9강 (연결리스트-개념과 기본 동작들) (0) 2022.02.22 자료구조 8강 (구조체 포인터, 동적 메모리 할당) (0) 2022.02.18 *자료구조 제 6강 (0) 2022.02.15 자료구조 제 5강 (0) 2022.02.15 자료구조 제 4강 (0) 2022.01.24