4. Requirements analysis and architecture design for address book implementation

This article implements the requirements analysis and architecture design of the address book product, focusing on the design of the structure level to facilitate code reading and maintenance.

1. Requirements analysis for address book implementation

1. Function list of address book

  1. add a person
  2. print all people
  3. delete a person
  4. find a person
  5. save document
  6. load file

2. Data storage information

  1. Personnel storage method --> doubly linked list
  2. File storage format --> format of personnel data
  3. Personnel information——> name, phone
    name: xxx, phone: xxx
    name: xxx, phone: xxx

2. The architecture design of the address book implementation

1. The design of the architecture should be analyzed from the bottom up.

  • Support layer: storage of data linked list, and reading and writing of files.
  • Interface layer: After reading the underlying linked list data, parse out the name and phone (unpacking), and after reading the name and phone, package and write it into the linked list data (packing). In addition, there is a unified functional interface layer, so that even if the file storage method changes, the upper layer design can still remain unchanged.
  • Business layer: business logic

For example:
add a user (function) —> enter user name and phone number (business logic) —> add through the interface layer —> insert into the linked list
insert image description here
insert image description here

2. Code and difficulties

2.1 The following difficulties were encountered in the process of code implementation

  1. secondary pointer
//插入
/*如果传入struct person *person,一开始person指向的是NULL,那么形参改变不会影响实参。
    参考值传递交换两个数。因此要传入的是二级指针struct person **pperson*/
int person_insert(struct person **pperson,struct person *ps)

//删除
/*如果传入struct person *person,最后person指向的是NULL,那么形参改变不会影响实参。
    参考值传递交换两个数。因此要传入的是二级指针struct person **pperson*/ 
int person_delete(struct person **pperson,struct person *ps)

//加载文件
//若使用struct person *person,刚开始空时候指向的是NULL,无法更改,因此要用二级指针
int load_file(struct person **pperson,int *count,const char *filename)

2. Use the state machine to read the file information and store it in the address book

//解析文本内的通讯信息
int parser_token(char *buffer,int length,char *name,char *phone)
int load_file(struct person **pperson,int *count,const char *filename)
  1. file manipulation functions
  2. Insertion and deletion of linked list

2.2 The specific code is as follows

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


//为了避免代码突然出现新定义的数字影响阅读,建议都放在宏定义中
#define NAME_LENGTH             16
#define PHONE_LENGTH            32
#define BUFFER_LENGTH           128
#define MIN_TOKEN_LENGTH        5
#define INFO    printf
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
//''''''''''''''''''''支持层''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

//do{...}while(0)一般用于宏定义中,只执行依次,并且条件不成立时退出
//宏不止一行,则在结尾加反斜线符号使得多行能连接上
//把item插入到list之前,(list)是因为传入的是*pperson,加括号是让指针优先,即(*pperson)->prev            
#define LIST_INSERT(item,list) do {
      
       \
    item->prev = NULL;              \
    item->next = list;              \
    if ((list) != NULL) (list)->prev=item;\
    (list) = item;                    \
}while(0);


#define LIST_REMOVE(item,list) do {
      
       \
    if (item->prev != NULL) item->prev->next=item->next;    \
    if (item->next != NULL) item->next->prev=item->prev;    \
    if (list == item) list = item->next;                    \
    item->prev =item->next=NULL;                            \
}while(0)


//person类,包含姓名、电话
struct person
{
    
    
    char name[NAME_LENGTH];
    char phone[PHONE_LENGTH];
    struct person *next;
    struct person *prev;
};

//通讯录,里面有person类,总人数
struct contact
{
    
    
    struct person *person;
    int count;  //人数
};


enum{
    
    
    OPEN_INSERT=1,
    OPEN_PRINT,
    OPEN_DELETE,
    OPEN_SEARCH,
    OPEN_SAVE,
    OPEN_LOAD
};

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
//''''''''''''''''''''接口层''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
//插入
int person_insert(struct person **pperson,struct person *ps){
    
     
    /*如果传入struct person *person,一开始person指向的是NULL,那么形参改变不会影响实参。
    参考值传递交换两个数。因此要传入的是二级指针struct person **pperson
    */
    if ( ps == NULL ) return -1;
    LIST_INSERT(ps,*pperson);
    return 0;
}


//删除
int person_delete(struct person **pperson,struct person *ps){
    
    
     /*如果传入struct person *person,最后person指向的是NULL,那么形参改变不会影响实参。
    参考值传递交换两个数。因此要传入的是二级指针struct person **pperson
    */  
    if ( ps == NULL ) return -1;
    LIST_REMOVE(ps,*pperson);
    return 0;
}


//查找
struct person* person_search(struct person *person,const char *name){
    
    
    struct person *item = NULL;
    for (item=person;item != NULL ;item=item->next){
    
    
        if (!strcmp(item->name , name)){
    
    
            break;;
        }
    }
    return item;
}

//遍历
int person_traversal(struct person *person){
    
    
    struct person *item = NULL;
    for (item=person;item != NULL ;item=item->next){
    
    
        INFO("name: %s,phone: %s\n",item->name,item->phone);
    }   
    return 0;
}

//保存文件
int save_file(struct person *person,const char *filename){
    
    
    FILE *fp=fopen(filename,"w");
    if (fp == NULL) return -1;
    struct person *item=NULL;
    for (item=person;item!=NULL;item=item->next){
    
    
        fprintf(fp,"name: %s,phone: %s\n",item->name,item->phone);
        fflush(fp);//fprintf是将数据存在缓冲区内,通过fflush刷新到磁盘中
    }

    fclose(fp);
}

//解析文本内的通讯信息
int parser_token(char *buffer,int length,char *name,char *phone){
    
    
    if (buffer == NULL ) return -1;
    if (length <MIN_TOKEN_LENGTH) return -2;  //文件结尾默认有一个文件结束标识符,大小不会超过5个字节

    //name: qiuxiang,telephone: 98765678123
    int i=0,j=0,status=0;

    //读取 name: qiuxiang
    for (i=0;buffer[i]!=',';i++){
    
    
        if (buffer[i]==' '){
    
    
            status=1;
        }
        else if(status==1){
    
    
            //将buffer[i]赋值给name[j],而后j++
            name[j++]=buffer[i];
        }
    }

    //读取 telephone: 98765678123
    status=0;
    j=0;
    for (;i<length;i++){
    
    
         if (buffer[i]==' '){
    
    
            status=1;
        }
        else if(status==1){
    
    
            //将buffer[i]赋值给name[j],而后j++
            phone[j++]=buffer[i];
        }       
    }

    INFO("file token : %s --> %s\n", name, phone);

    return 0;
}
//加载文件
//若使用struct person *person,刚开始空时候指向的是NULL,无法更改,因此要用二级指针
int load_file(struct person **pperson,int *count,const char *filename){
    
    
    FILE *fp=fopen(filename,"r");
    if (fp == NULL ) return -1;

    //feof():侦测是否读取到了文件尾,如果已到文件尾则返回非零值,其他情况返回 0
    while (!feof(fp)){
    
    
        char buffer[BUFFER_LENGTH]={
    
    0};

        //fgets(str,n,fp):从 fp 所指文件中读入 n-1 个字符放入 str 为起始地址的空间内;如果在未读满 n-1 个字符时,则遇到换行符或一个 EOF 结束本次读操作,并已 str 作为函数值返回.
        fgets(buffer,BUFFER_LENGTH,fp);

        int length=strlen(buffer);
        INFO("legth :%d\n",length);

        //name: qiuxiang,telephone: 98765678123
        char name[NAME_LENGTH]={
    
    0};
        char phone[PHONE_LENGTH]={
    
    0};

        if (0 != parser_token(buffer,length,name,phone)){
    
    
            continue;
        }

        struct person *p=(struct person*)malloc(sizeof(struct person));
        if (p == NULL) return -2;

        //void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字节到存储区 str1
        memcpy(p->name,name,NAME_LENGTH);
        memcpy(p->phone,phone,PHONE_LENGTH);

        person_insert(pperson,p);

        (*count)++;
    }
    fclose(fp);
    return 0;
}

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
//''''''''''''''''''''业务逻辑层''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

int insert_entry(struct contact *cts){
    
    
    if (cts ==NULL) return -1;  //输入空信息错误

    struct person *p=(struct person*)malloc(sizeof(struct person));
    if (p == NULL) return -2;  //内存分配错误

    //name
    INFO ("Please Input Name:\n");
    scanf("%s",p->name);

    //phone
    INFO ("Please Input Phone:\n");
    scanf("%s",p->phone);

    //add to person
    if (0 !=person_insert(&cts->person,p)){
    
    
        free(p);
        return -3;
    }
    cts->count++;

    INFO("Insert Success\n");
    return 0;
}

int print_entry(struct contact *cts){
    
    
    //打印通讯录中的信息
    if (cts == NULL ) return -1;

    person_traversal(cts->person);
}

int delete_entry(struct contact *cts){
    
    
    //删除通讯录中某个人的信息
    if (cts == NULL ) return -1;

    INFO("Please Input Name:\n");
    char name[NAME_LENGTH] ={
    
    0};
    scanf("%s",name);

    //判断输入的name是否存在通讯录中
    struct person *ps=person_search(cts->person,name);
    if (ps == NULL) {
    
    
        INFO("Person don't Exit\n");
        return -2;
    }

    //删除
    person_delete(&cts->person,ps);
    free(ps);

    return 0;
}

int search_entry(struct contact *cts){
    
    
    //查找通讯录中某个人的信息
    if (cts == NULL ) return -1;

    INFO("Please Input Name:\n");
    char name[NAME_LENGTH] ={
    
    0};
    scanf("%s",name);

    //判断输入的name是否存在通讯录中
    struct person *ps=person_search(cts->person,name);
    if (ps == NULL) {
    
    
        INFO("Person don't Exit\n");
        return -2;
    }

    INFO("name: %s, phone: %s",ps->name,ps->phone);

    return 0;
}

int save_entry(struct contact *cts){
    
    
    //将通讯录保存
    if (cts==NULL) return -1;

    INFO("Please Input Save Filename:\n");
    char filename[NAME_LENGTH]={
    
    0};
    scanf("%s",filename);

    save_file(cts->person,filename);
}

int load_entry(struct contact *cts){
    
    
    //加载文件中的通讯录
    if(cts==NULL)  return -1;

    INFO("Please Input Load Filename:\n");
    char filename[NAME_LENGTH]={
    
    0};
    scanf("%s",filename);

    load_file(&cts->person,&cts->count,filename);
}

void menu_info() {
    
    

	INFO("\n\n********************************************************\n");
	INFO("***** 1. Add Person\t\t2. Print People ********\n");
	INFO("***** 3. Del Person\t\t4. Search Person *******\n");
	INFO("***** 5. Save People\t\t6. Load People *********\n");
	INFO("***** Other Key for Exiting Program ********************\n");
	INFO("********************************************************\n\n");

}


int main(){
    
    
    struct contact *cts =(struct contact *)malloc(sizeof(struct contact));
    if(cts == NULL) return -1;

    memset(cts,0,sizeof(struct contact));  //初始化cts为0
    while(1){
    
    
        menu_info();
        int select=0;
        scanf("%d",&select);
        switch (select)
        {
    
    
            case OPEN_INSERT:
                insert_entry(cts);
                break;
            case OPEN_PRINT:
                print_entry(cts);
                break;
            case OPEN_DELETE:
                delete_entry(cts);
                break;
            case OPEN_SEARCH:
                search_entry(cts);
                break;
            case OPEN_SAVE:
                save_entry(cts);
                break;
            case OPEN_LOAD:
                load_entry(cts);
                break;
            default:
                goto exit; 

        }

    }

    exit:
        return 0;
        free(cts);
}


Guess you like

Origin blog.csdn.net/Ricardo2/article/details/130787368