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 |
---|