-
*자료구조 제 6강자료구조 2022. 2. 15. 21:55
phonebook03
-잘못된 명령어에 대해서 적절히 반응
-저장된 사람의 수가 배열의 용량을 초과할 경우 동적 메모리 할당으로 배열의 크기를 키움
구분문자를 이용하여 하나의 긴 문자열을 작은 문자열들로 자르는 일을 문자열 tokenizing이라고 부른다. 잘라진 작은 문자열들을 보통 token이라고 부른다.
strtok
- 원본 문자열을 변화시킴(구분자 자리에 '\0'을 삽입) -> 원본 문자열을 보존해야 한다면 문자열을 복사한 후 strtok을 실행해야함
- 문자열을 선언할 때 string literal (ex. char *str = "Hello world";)로 선언 할 경우 수정 불가능 하므로 strtok 오류 발생 (char str[] = "Hello world"; 가능)
- strtok은 새로운 배열을 생성하지 않음 -> _strdup와는 다름
/* * 2022-02-15 * 전화번호부v3.0 * 배열 재할당, 라인 단위 입력과 문자열 tokenizing */ #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #define INIT_CAPACITY 3 //배열 재할당을 테스트하기 위해 일부러 아주 작은 값으로 설정 #define BUFFER_SIZE 50 void init_directory(); int read_line(char str[], int limit); void process_command(); void load(char* fileName); void add(char* name, char* number); void status(); void find(char* name); void remover(char* name); void save(char* fileName); void reallocate(); char** names; // char* 타입의 배열의 이름이므로 char** 타입의 변수 char** numbers; int capacity = INIT_CAPACITY; int n = 0; char delim[] = " "; int main() { init_directory(); //이 함수에서 배열 names와 numbers를 생성한다. process_command(); //사용자의 명령을 받아 처리하는 부분을 별개의 함수로 만들었다. return 0; } void init_directory() { names = (char**)malloc(INIT_CAPACITY * sizeof(char*)); numbers = (char**)malloc(INIT_CAPACITY * sizeof(char*)); } //line 단위의 입력은 fgets, getline 등의 함수들을 이용하여 할 수도 있다. //많은 c프로그래머들은 직접 함수로 만들어서 쓰는 경우가 많음 (각 상황의 미묘한 차이 때문에) int read_line(char str[], int limit) //limit보다 더 긴 경우에는 뒷부분이 짤린다. { int ch, i = 0; while ((ch = getchar()) != '\n') //줄바꿈 문자가 나올 때 까지 읽는다. if (i < limit - 1) //배열의 용량을 초과하지 않을 때만 저장한다. str[i++] = ch; str[i] = '\0'; //마지막에 null character을 추가한다. 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 (strcmp(command, "read") == 0) { argument1 = strtok(NULL, delim); if (argument1 == NULL) { printf("File name required.\n"); continue; } load(argument1); //파일명을 인자로 주면서 load를 호출한다. } else if (strcmp(command, "add") == 0) { argument1 = strtok(NULL, delim); //명령어에 이어지는 2개의 토큰은 각각 이름과 전화번호이다. argument2 = strtok(NULL, delim); 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 arguments.\n"); continue; } remover(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)) { //file에서 이름 읽음, 파일의 끝(End Of File)에 도달할 때 까지 반복해서 이름과 전화번호를 읽어서 배열에 저장 fscanf(fp, "%s", buf2); //file에서 전화번호 읽음 add(buf1, buf2); } fclose(fp); } void add(char* name, char* number) { if (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); //strdup를 사용해야하는 이유: 전 함수의 지역변수인 buf1과 buf2가 name과 number이기 때문에 load함수가 return되면 사라지기 때문 numbers[i + 1] = _strdup(number); } 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); if (index == -1) printf("No person named '%s' exists.\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; //name과 동일한 사람이 있는 경우 해당 인덱스인 i리턴 } return -1; //name과 동일한 사람이 없는 경우 -1리턴 } 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++) { 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"); 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*)); //크기가 2배인 배열들 할당 char** tmp2 = (char**)malloc(capacity * sizeof(char*)); for (i = 0; i < n; i++) { //원본 배열 names와 numbers의 값을 새로운 배열에 복사 tmp1[i] = names[i]; tmp2[i] = numbers[i]; } free(names); //원본 배열들은 더이상 필요 없으므로 free함수를 사용하여 반환 free(numbers); names = tmp1; //names와 numbers가 새로운 배열을 가리키도록 한다. numbers = tmp2; }
*실행이 제대로 안되는 부분있음 -> 후에 고칠 예정
'자료구조' 카테고리의 다른 글
자료구조 8강 (구조체 포인터, 동적 메모리 할당) (0) 2022.02.18 자료구조 제 7강 (구조체) (0) 2022.02.17 자료구조 제 5강 (0) 2022.02.15 자료구조 제 4강 (0) 2022.01.24 자료구조 제 3강 (0) 2022.01.09