LINUX 入门 2
day5 20240428 耗时:135min
课程链接地址
第2章 linux c 通讯录
接上回,两个文件操作
c语言开发知识点很综合了,掌握上一章和这一章差不多了
C基础
-
二级指针问题
- 如果不改变指针,只通过指针访问,一级就行;
- 如果涉及通过指针改变内容用二级,对链表插入,删除链表元素,可能空链表情况需要二级(指针的指针)
试了一下不用二级指针**ppeople,用一级指针*people,涉及改变空指针的delete和insert有什么bug:
- insert:会插不进去,宏那里LIST_INSERT有问题
- delete: 还行,因为先search找不到;但貌似删只剩一个的时候,有问题,删不掉而且name改了会
-
函数如果传入形参改值,不用值传递,用引用传递类似& 用*
比如
int change(int *cnt)
而不是int change(int cnt)
, 然后是(*count)++接受一个指向整数的指针
cnt
作为参数,并对指针所指向的整数进行递增操作 -
常见错误不是
struct *contacts cts
是struct contacts *cts
-
不建议用goto,因为循环里goto跳来跳去,嵌套很乱
7 文件保存与加载的接口层实现
想看效果可以
cat b.txt
-
保存
- 用循环,没用递归实现save,以键值对的struct 存到file里,遍历一个存一个
- 接口和支持层放在一起,然后再业务层
- 文件操作自己查:
- fprintf打印到文件里,printf打印到command终端
- fprintf要再fflush,因为fprintf写道缓存cache里要flush刷新到磁盘里,真正做到落盘
- fclose
-
加载
-
feof(fp)是不是结尾,是返回0
-
buffer大小,注意这种数字统统放前面宏定义define,不然很容易乱掉!!
-
读行+解析内容:
fgets(buffer, 长度,fp) 读一行,然后分装parser解析出name phone用上一章的状态机统计文字个数,妙啊!!!!!!
长这样
name: qiuxiang,telephone: 98765678123
遇到空格状态跳1,默认0, 再用person_insert插入到ppeople
-
完整代码
int save_file(struct person *people, const char *filename){
FILE *fp = fopen(filename, "w");
if(fp == NULL) return -1;
struct person *item = NULL;
for(item = people; item != NULL; item = item->next){
fprintf(fp, "name: %s, phone: %s", item->name, item->phone);
fflush(fp); //从缓存刷到磁盘里
}
fclose(fp);
}
//用来封装读一行以后解析出name和phone过程
int parser_token(char *buffer, int length, char *name, char *phone){
// 提取name
if(buffer ==NULL) return -1;
int i = 0,j =0, status = 0; // 默认0, 遇到空格开始提取,变成1
for(i = 0; buffer[i]!= ','; i++){
if(buffer[i] ==' ') status =1;
else if(status ==1) name[j++] = buffer[i];
}
// 提取phone
status = 0, j =0;
for(; i< length; i++){
if(buffer[i] == ' ') status =1;
else if(status == 1) phone[j++]= buffer[i];
}
INFO("file token: %s --> %s\n", name, phone);
return 0;
}
//count要改,引用传递
int load_file(struct person **ppeople, int* count, const char *filename){
FILE *fp= fopen(filename, "r");
if(fp == NULL) return -1;
while(!feof(fp)){
char buffer[BUFFER_LENGTH] = {0};
fgets(buffer, BUFFER_LENGTH, fp); //读一行 以后解析
char name[NAME_LENGTH] = {0};
char phone[PHONE_LENGTH] ={0};
//失败了就跳了
if(0!= parser_token(buffer,strlen(buffer),name,phone) continue;
// 插入,借助pereson_insert函数
struct person *p = (struct person *) malloc(sizeof struct person);
if(p== NULL) return = -2;
memcpy(p->name, name, NAME_LENGTH);
memcpy(p->phone, phone, PHONE_LENGTH);
person_insert(ppeople, p);
(*count)++;
}
fclose(fp);
return 0;
}
8 文件保存与加载的业务层实现
业务层entry,“entry” 通常指的是程序的入口点,即程序开始执行的地方。
默认other key的操作default是exit,不建议用goto,因为循环里goto跳来跳去,嵌套很乱,写了也行
int save_entry(struct contacts *cts){
if(cts == NULL) return -1;
INFO("Please Input Save Filename :\n");
char filename[NAME_LENGTH] = {0};
scanf("%s",filename);
save_file(cts->people,filename);
}
int load_entry(struct contacts *cts){
if(cts ==NULL) return -1;
INFO("Please Input Load Filename :\n");
char filename[NAME_LENGTH] = {0};
scanf("%s", filename);
load_file(&cts->people,&cts->count, filename);
}
main的switch里面最后加上
default:
goto exit;
}
}
exit:
free(cts);
return 0;
}
9 通讯录调试与运行
不知不觉已经写了三百多行,c语言开发知识点很综合了,掌握上一章和这一章差不多了
重点是产品和分层的思想:产品——分层——业务层(用户操作)-接口层(隔离底层数据实现和上层用户操作)- 系统支持层(数据结构实现+文件操作)
分层好处:调试很有用,分层排查错误!!!
我出bug了:
-
5保存它不跳提示信息,为啥:调不出来啊啊啊啊:绝了,终于找出了bug,switch里面的case后面竟然直接打了break服了!!!
-
6加载只有两行,它出来三行,最后一行是乱码,因为默认文件结尾有结束符EOF
加上对strlen(buffer)的判断,如果太短,不会解析了,改load_file和parser_token