본문 바로가기

C_Data Structure_Algorithm/C telephone v4.0

C 전화번호부 v4.0_2)

load


if( read_line ( fp, buffer, BUFFER_LENGTH) < = 0 ) : 더 이상 읽을 게 없으면, break

만일 유효한 정보가 있다면, name ~ group 정보를 읽고, 그 기준은 # 구분자를 기준으로 적용하는 것이다.

첫번째 ~ 4번째 token 이 각각 name ~ group 으로 들어가는 것이다.

기억할 것은, 존재하지 않는 항목은 한칸의 공백 문자이다.

이름이 없는 사람은 없으니, 해당 사항이 없지만,

number ~ group 같은 경우, 없을 수 있으므로,

그 경우에는 한 개의 공백 문자로 이루어진 string 이 되는 것이다.

그리고 다 읽게 되면, 그것을 add ( ) 를 통해 directory에 추가한다.

add


4개의 문자를 받는다.

그리고, 알파벳 순서대로, 이름을 저장하고,

그 위치에, 우리가 입력받은 한 사람의 4개의 정보를 구조체 한으로 넣는 것이다.

directory[ i +1 ] = directory [ i ] 라는 것은, 구조체도 = 를 통한 취합문이 가능하다는 것이다.

directory[ i +1 ]. number = strdup( number );

여기서 기억해야 할 것은, 만일 이 사람의 전화번호가 없다면, number 는 공백, 즉 1개의 공백 문자로 이루어진 string 이 된다.

read 와 add 함수의 차이를 볼 필요가 있다.

read 의 경우에는, 뒤에 파일 이름이 나오고, 파일 이름에는 공백이 존재하지 않으므로, strtok 을 통해서 읽어낸다.

반면, add 뒤에는 4개의 정보가 나오고, 그 사이사이에 공백이 포함되거나, 각 정보안에 공백이 있을 수 있다. 그래서 strtok 이 아니라, 사용자 정의함수를 따로 만들어서, 정보를 읽어내야 한다.

ex. $ add Hong Gil-Dong 의 경우, 중간에 공백이 있는 사람의 이름을 압축해서, 불필요한 공백 버리고, !!!! 단어 사이에 여러개 공백은 1개의 공백으로 압축시켜서, 실제 저장할 때는

Hong Gil-Dong 이렇게 저장하자는 것이다.

compose_name 함수는 command_line 에서, 앞뒤에 붙은 불필요한 공백들을 제거하고, 단어와 단어 사이의 2개 사이의 공백이 있을 때, 그것을 하나의 공백으로 축약하는 것. name_str 이라는 이름으로 축약하기.

compose_name


ptr = strtok( NULL, " " )

ptr 은 2번째 token 에 해당한다. 1번째 token 은 command ( 명령어 ) 이니까.

ptr 은 입력라인의 2번째 token

if( ptr == NULL) return 0;

즉, 2번째 token 이 NULL 이라는 것은, 사람의 이름이 아예 없다는 것이다.

그렇지 않으면, ptr 은 이름의 첫번째 단어가 되는 것이다.

strcpy( str, ptr)

즉, 이름이 여러개의 단어로 구성되어 있을 때, 그것을 str 에다가 복사하고,

그리고, 이름의 길이는 첫번째 토근의 길이만큼 될 것이다. length += strlen( ptr)

그리고, 이름은 여러개의 단어로 구성될 수 있으므로,

while ( ( ptr = strtok( NULL , " " ) ) ! = NULL )

문장 끝 에 도달할때까지 반복하기

if ( length + strlen( ptr ) + 1 < limit ) : buffer 의 overflow 를 방지하기 위한 것이다.

length 는 현재 str 에 써놓은 단어의 길이

strlen( ptr )은 새로운 token 의 길이.

이것끼리 더했을 때 int limit 보다 커지면, overflow 생기는 것이므로, 그렇지 않을때까지만 쓰겠다.

즉, 예외적인 경우, 이름의 길이가 너무 길어서, buffer size 를 초과하는 경우에 대한 대처.

+1 을 해주는 것은 우리가 끝에 NULL 을 넣어주어야 하므로

str[ length ] = '\0'

현재 우리는 ptr 을 통해 첫번째 단어를 읽고 그것을 str 에 넣었다.

그리고 다음 ptr 로 두번째 단어를 읽은 상태이다.

이제 이 두개의 단어를 이어야 하는 것인데, 문제는 단어와 단어 사이에 공백이 필요하다. 이것을 str[ length ++ ] = " " 을 사용해서, 공백을 넣어주는 것.

그리고 그 공백 뒤에 strcat( str, ptr ) 을 통해, 2번째 단어를 첫번째 단어 + 공백 . 뒤에 이어주는 것이다.

strcat 에서 중요한 사항이 있다.

strcat( str, ptr ) 의 경우, str 과 ptr 은 끝에 NULL 이 있다는 가정하에 작동하는 것이다.

strcpy( str, ptr) 을 하면,

자연스레 str 뒤에 null 이 있다.

문제는 우리가 str 뒤에 공백을 추가하기 위해

str[ length ++ ] = ' ' . 을 통해, NULL 을 공백으로 바꿔버렸다.

그래서 str[ length ] = '\0' 으로, NULL 을 다시 추가하는 것이다.

즉, 우리가 John 을 입력하게 된다면

strcpy( str, ptr ) 까지는 John\0 이 있는 것이고,

str[ length ++ ] = ' ' 에서는

John' ' 가 되는 것 ( NULL 이 끝에 존재하지 않는다 )

str[ length ] = NULL

John' '\0 를 통해, 끝에 NULL 을 넣는 것이다.

ex. s k y \0 를 strcpy를 통해 str 에 넣었다.

length += strlen(ptr)

str[length] >> length : 3

str[ 0 ] = s

str[1 ] = k

str[ 2] = y

str[ 3 ] = \0

str[length ++ ] = str[ 3 ] = " " ( 공백 )

str[length ] = str[4] = NULL

Handle_add


이제 우리는 사람의 이름까지 받은 것이고,

사람의 이름을 받으면 이후 Phone, Email, Group 을 받아야 한다.

그리고, 우리가 무언가 입력하게 되면, 그것을 끝까지 읽어서 stdin 에 넣는 것이다.

만일 우리가 read_line 이 아니라, scanf 를 쓴다고 할때

엔터를 치게 되면, 사용자가 정보를 입력할 때까지, 기다리게 되겠지..ㅜㅜ

그리고, 우리가 정보를 다 읽게 되면, 읽은 정보를 이제 넣어주면 된다.

add 가 요구하는 바가 있다.

만약 그 항목 ex. name, character 등이 요구하는 내용이 없다면

공백 한칸 짜리 문자를 제공해주어야 하는 것이다.

그래서 이 add 를 load 함수에서 호출 할 때랑, handle_add 에서 호출할 때 일관성이 생기기 때문이다.

위에서 add 부분이 다소 복잡하게 생겼다.

name, 즉, 이름의 경우에는, 일반적으로 항상 입력하니까 상관없다.

그러나 number, email, group 등은 사용자가 입력하지 않은 경우가 있을 수 있다.

우선, 사용자가 입력을 한 경우

즉, strlen( number ) > 0 ? number: empty

즉, 입력했으면 number, 안했으면 empty ( strlen( number ) ≤ 0 ) 즉, 사용자가 아무것도 입력하지 않고, 엔터칠 경우,

이 경우, 한칸의 공백 문자로 이루어진 문자열 int empty = " " 를 매개변수로 넘겨주는 것이다.

즉, 공백 문자 하나로 이루어진 string 인 empty를 이미 만들어놓고, 이것을 넘겨주는 것이다.

( char * )으로 당연히 type 변환을 해주고,

이를 통해서,

add 입장에서는 존재하는 항목은 존재하는 항목으로, 존재하지 않는 항목은 길이가 1인, 하나의 공백으로 이루어진 문자열로 출력을 해준다.

Save


save 같은 경우에도, 한명 씩 쓰면 된다.

fprintf 문으로, %s 로,

name ~ group 같은 경우, 비록 항목이 없어도, 공백 하나로 이루어진 문자열이 저장되어 있으므로, 여기서는 그냥 항목이 있냐 없냐를 신경쓰지 않고, 출력만 하면 된다.

Search , Print_person


print_person ??

ex . find( John ) 을 하면,

화면에 John 에 대한 정보를 출력하게 하는 함수.

Remove


Status , Find



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

#define CAPACITY 100
#define BUFFER_LENGTH 100

typedef struct person{
	char *name;
	char *number;
	char *email;
	char *group;
} Person ;
// 이렇게 한 사람의 정보를 구조체로 정의하고, 이것을 directory 안에
// 모든 사람의 정보를 저장하는 것이다. 

Person directory[CAPACITY];

int n = 0;
int read_line( FILE *fp, char str[], int n ); 
void add( char *name, char *number, char *email, char *group)
int compose_name( char str[], int limit);
void load( char *fileName);
void handle_add(char *name);
void save( char *fileName);
int search(char *name);
void print_person(Person p);
void remove( 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)
			continue;
			// 어떠한 명령어도 입력하지 않았을 때, 다시
		command = strtok( command_line, " ");
		// 명령어가 무엇인지 판단한다 ex. add 인지, find 인지
		
		if( strcmp ( command , "read") == 0 ){
			argument = strtok(NULL," ");
			// 명령어 다음에 나오는 것은, 파일 명.
			if( argument == NULL){
				printf("Invalid Argument\n");
				continue;
			}
			
			load(argument); 
		} // read if 문 
		
		else if( strcmp( command, "add")== 0 ){
			
			if( compose_name(name_str, BUFFER_LENGTH)<= 0){
				printf("Name required\n");
				continue; 
			}
			
			handle_add(name_str);
		} // add else if 문 
		
		else if( strcmp( command, "find") == 0 ){
			
			if( compose_name( name_str, BUFFER_LENGTH) <= 0){
				printf("Name required\n");
				continue;
			}
			
			fine(name_str);
		}// find else if 문 
		
		else if( strcmp( command, "delete") == 0 ){
			
			if( compose_name( name_str, BUFFER_LENGTH) <=0){
				printF("Invalid argument\n");
				continue;
			}
			
			remove(name_str);
		}// status else if 문
		
		else if( strcmp( command, "status") == 0){
			
			status();
		}
		
		else if( strcmp( command , "save") ==0 ){
			argument = strtok( NULL , " ");
			
			if( strcmp( argument, "as") != 0 ){
				printf("Invalid argument\n");
				continue;
			}
			
			argument = strtok( NULL, " ");
			if( argument == NULL){
				
				printf("Invalid argument\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 = getchar() )!= '\n' && ch != EOF )
	// 그 라인에서 엔터를 치거나
	// 파일을 읽을 경우, 파일의 끝에 도달할 경우 : ch != EOF 
		if ( i < n)
			str[i++] = ch;
			// str 배열에, 읽은 문자를 하나씩 넣는 것이다.  
			
	str[i] = '\0'; // 마지막 문자열 끝에 NULL 넣기.  
	return i;
}

int compose_name( char str[], int limit){
	
//	왜 compoese_name 이라는 함수를 만들어주는 것일까.
//	read 경우에는, 뒤에 파일 이름이 나오고,
//	파일 이름에는 공백이 존재하지 않으므로, strtok을 통해 읽어낸다.
	
//	반면, add 뒤에는 4개의 정보가 나오고, 그 사이사이에 공백이 있거나
//	각 정보안에 공백이 있을 수 있다.
//	그래서 strtok이 아니라, 사용자 정의함수를 따로 만들어서
//	정보를 읽어내야 한다. 

//$add Hong    Gil-Dong의 경우, 중간에 공백이 있는 사람의 이름을 
//압축해서 , 불필요한 공백은 버리고, 
//단어 사이에 여러개 공백은 1개의 공백으로 압축시켜서
//실제 저장할 때는 Hong Gil-Dong 이렇게 저장하는 것이다.
//
//compose_name 함수는 command_line 에서 ,
//앞뒤에 붙은 불필요한 공백들을 제거하고,
//단어와 단어 사이의 2개 사이의 공백이 있을 때
//그것을 하나의 공백으로 축약하는 것
//name_str 이라는 이름으로 축약하기 
	 
	 
	char *ptr;
	int length = 0;
	
	ptr = strtok(NULL, " ");
	// ptr 은 2번째 token에 해당한다. 1번째 token은 command, 즉, 명령어에 해당
	 
	if( ptr == NULL)
		return 0;
		// 2번째 token이 NULL 이라는 것은, 사람의 이름이 아예 없다는 것이다.  
		
	strcpy( str, ptr);
	// ptr 이 정상적으로 통과되면, ptr은 파일 '사람 이름' '의 첫번재 단어가 되는 것이다.  
	length += strlen(ptr);
//	복사된 이름의 길이는 첫번째 토근의 길이만큼 될 것이다.

//이름은 여러개의 단어로 구성될 수 있다.
//그러므로 우리가 입력한 문장의 끝에 도달할 때까지 반복하기

	while( (ptr = strtok( NULL, " ")) != NULL){
		
		if( length + strlen(ptr) + 1 < limit ){
//	if 문은 buffer의 overflow를 방지하기 위해서이다.
//length는 현재 str에 써놓은 단어의 길이
//strlen(ptr)은 새로운 token의 길이
// + 1은 문자열 마지막에 붙는 NULL 문자
// 즉, 예외적인 경우, 이름의 길이가 너무 길어서
// buffer size를 초과하는 경우에 대한 대처  
 
			str[length++] = ' ';
//strcat 함수의 중요한 조건이 있다. 그것은 바로, str, ptr ( 아래)의 경우
//끝에 공백 " "문자가 들어가 있어야 한다는 것. 
//그래서 우선 끝에 공백문자를 추가한다.
// 그런데 문자열의 기본 조건은 끝에 NULL 이 들어가는 것
// 그래서, 공백 문자 이후에 다시 NULL 문자를 추가해주는 것이다.  
			str[length] = '\0';
			strcat(str, ptr);
			length += strlen(ptr);
// 새로운 문자열을 str 에 갖다 붙였기 때문에, 그 길이만큼 증가시켜줘야 한다. 
		}
	} 
	
}

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  
			break;
			
		// 만일 유효한 정보가 있다면, name ~ group 정보를 읽고
//		그 기준은 # 구분를 기준으로 적용하는 것이다.
//		첫번째 ~ 4번째 token이 각각 name ~ group으로 들어가는 것이다.
//		기억할 것은 존재하지 않는 항목은 한칸의 공백 문자이다.
//		이름이 없는 사람은 없으니, 해당 사항이 없지만,
//		number ~ group 같은 경우, 없을 수 있으므로
//		
//		그 경우에는 한 개의 공백 문자로 이루어진 string 이 되는 것이다.
//		그리고 다 읽게 되면, 그것을 add( )를 통해
//		directory에 추가한다. 
		 
		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--;
	}
//	알파벳 순서대로 이름을 저장하기 위한 코드.
//	이후, 우리가 입력받은 한 사람의 4개의 정보를 구조체 안으로 넣는다.
	
	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++; 
//	한사람의 정보를 추가했으므로 현재 저장된 사람의 수를 의미하는
//	n을 1 증가시킨다. 
}

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);
	
//	이제 우리는 사람의 이름까지 받은 것이고,
//	사람의 이름을 받으면, 이후 Phone, Email, Group을 받아야 한다. 
	
//우리가 read_line 이 아니라, scanf 를 쓴다고 할 때
//엔터를 치게 되면, 사용자가 정보를 입력할 때까지, 기다리게 될 것이다. 
	printf(" Email : ");
	read_line(stdin, email, BUFFER_LENGTH);
	
	printf(" Group : ");
	read_line(stdin, group, BUFFER_LENGTH);
	
//	사람의 정보에 있어서 name 은 항상 입력한다. 
//	그러나, number, email 등의 정보는 없을 수도 있다.  
// strlen(number) <=0 즉, 아무것도 입력하지 않고 엔터치면
//이 경우, 한칸의 공백 문자로 이루어진 문자열 empty를 매개변수로 넣는 것
//즉, 공백 문자 하나로 이루어진 string인 empty를 이미 만들어두고, 
//이것을 넘겨주는 것이다  

//( char * )으로 당연히 type 변환도 해주고 
 
	add( name, (char*)(strlen(number) > 0 ? number : empty),
				(char*)(strlen(number) > 0 ? number : empty),
				(char*)(strlen(number) > 0 ? number : 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#", 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;
}


//find(John)을 하면, John에 대한 정보를 출력하게 해주는 함수 
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 remove( char *name){
	
	int i = search(name);
	if( i == -1){
		printf("No person named\n");
	}
	
	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 people\n", n);
	}
}

void find( char *name ){
	
	int index = serach(name);
	
	if( index == -1)
		printf("no such person\n");
	
	else
		print_person( directory[index]);
}

'C_Data Structure_Algorithm > C telephone v4.0' 카테고리의 다른 글

C 전화번호부 v4.0_1)  (0) 2020.03.03