ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • *자료구조 제 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
Designed by Tistory.