实验5链表操作:
题目:
编写一个链表综合程序,实现以下功能:
- 编写一个链表的构建函数输入任意多条的长度不等的字符串,结束的条件是
直接回车,用动态链表方式处理保存各个字符串, - 编写一个显示函数, 调用即显示前面所有输入过的字符串,
- 编写一个查询函数用以查询前面输入的字符串里是否有某字符串。
- 编写一个文件保存函数, 调用后链表上的所有字符串保存在文件data.txt里.
- 编写一个读文件添加链表数据的函数, 调用后,能把文件data.txt里的字符添加
到当前的链表上 - 构建功能选择菜单, 通过菜单选择运行以上哪个函数的功能.
(广东工业大学计算机)
第一部分:创建链表与打印
首先我立马打了几个框架,第一次测试:失败
1 |
|
运行结果1:
- 无输出
思考:
1、经过测试打印和创建都有问题
修改:
1、更换表达方式
1 | while(gets(node->s)&&strcmp(node->s,"")!=0) |
改成
1 | while(gets(node->s)&&strlen(node->s)!=0) |
2、更改打印的代码
1 | void print(strings* p)//打印 |
删掉了一行p = p->next;
这样才能输出第一个节点
3、参考运行成功代码
发现原来看的别人的博客写的代码是错的,根本运行不了(流汗)。
第二次测试
1 |
|
运行成功。
打印和创建函数完成。
第二部分:文件读写和添加节点
添加节点放在这里是因为在添加节点的同时顺便写入文件。
1、读取文件
1 | FILE *fp=fopen(data,"r");//data是文件名 |
和创建链表相同,每次打开文件都要重新读取重新创建链表;不同的是,输入不一样,变成了从文件读取,使用fscanf()
函数,完整函数如下
解释:
fscanf()
:从文件中读取字符串,但是在遇到第一个空格和换行符时,它会停止读取
1 | struct strings *readfile(char *data)//读取文件并且创建链表 |
2、保存到文件
这里要了解几个函数(菜鸟教程)
①fputc()
函数
函数 fputc() 把参数 c 的字符值写入到 fp 所指向的输出流中。如果写入成功,它会返回写入的字符,如果发生错误,则会返回 EOF。
②fputs()
函数
函数 fputs() 把字符串 s 写入到 fp 所指向的输出流中。如果写入成功,它会返回一个非负值,如果发生错误,则会返回 EOF。
③fprintf()
函数
int fprintf(FILE *fp,const char *format, …) 函数把一个字符串写入到文件中。
1 | void save(struct strings *p,char *data)//保存到文件 |
3、添加节点
添加手动输入的字符
1 | struct strings *addtofile(struct strings *head)//添加节点 |
1 | struct strings *addtolist(struct strings *head,struct strings* news)//添加节点 |
4、程序主体
这是实现文件读写的链表程序,read file读取文件并且输出,在add:后面输入字符,如果回车两次,即没有输入,则会打印整个链表
程序如下
1 |
|
第三部分:菜单实现
一、两个键盘读取函数:
1、检测输入按键函数getch()
函数返回按键的数值;
UP = 72
DOWN = 80
LEFT = 75
RIGHT = 77
ENTER = 13
ESC = 27
//按键数值
2、函数kbhit()
检查当前是否有键盘输入,若有则返回一个非0值,否则返回0 。
函数名:kbhit()(VC++6.0下为_kbhit())
用法:int kbhit(void);
包含头文件: include <conio.h>
两者区别:
kbhit() 在执行时,检测是否有按键按下,有按下返回非0值,一般是1;没有按下返回0;
是非阻塞函数。
getch() 在执行时,检测按下什么键,如果不按键该函数不返回,是阻塞函数
二、设置文本样式
我们需要制作一个菜单,使用方向键选择选项,并且高亮显示,这里就需要设置字体格式,使用SetConsoleTextAttribute()
函数可以设置文字颜色和高亮。
1 | SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x70);//高亮显示 |
1 | SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x07);//正常显示 |
这里是简短的测试程序
1 |
|
程序结果
三、显示菜单并且高亮
读取键盘完成了,就需要显示出菜单界面,我们设置一个char数组来储存。
1 | char MenuText[7][30]={ |
将他打印出来的话,就是正常的样子。但是我们需要让他高亮,这里就需要思考,如何让特定的选项高亮呢?这里我们在打印菜单函数coutmenu()
函数里面设置了一个变量highlight
,他的值是多少代表是第几行需要高亮。接着我们使用for循环7次(有7行)一次输出,这样菜单里面就有一个选项是高亮的。
1 | void coutmenu(int highlight)//打印菜单函数 |
如图所示(假设这里highlight=2,那么第二行就会高亮)
不知道大家有没有想到,怎么让他按照我们的意愿高亮。想不到也没关系,我也想不到,这里我们通过反复清空屏幕,再反复输出的方式,来实现上下移动。
四、实现选项移动
因为我们要反复输出我们就需要清空屏幕,这里我们用system("cls");
来清空屏幕。
同时我们需要使用sleep()
函数来让程序暂停,如果数值设置太短,我们按方向键的时候,可能已经经过了好多个循环,相当于你按了n多次方向键,会跳的很快。
1 | for(;;){ |
(这里是另一个函数,所以高亮显示的行号用high
表示)
这个程序的巧妙之处在于每次收到上下的信号就会对变量high做出运算,DOWN的情况是大于7则变成1,小于7则加1。这样在第七行(最后一行),再按一次down就会回到第一行。不过这个回退不是必要的,也可以不加。
五、按回车执行对应程序
在前面的if语句后面接上 if 语句,判断是否回车,回车符是'\r'
,接着用swich语句来判断情况执行语句。
1 | else if(c=='\r'){//判断回车 |
效果如图,按回车后才回显示
第四部分:加上查找功能
没有太大难度,基础的查找操作
1 | void find(struct strings *head)//查找 |
第五部分:把菜单整合到链表程序中
所有工作都分工做完了,现在就剩下整合合微调了。
一、判断语句设置
这是整合后的Switch函数,这里的函数一定要调用对,别搞错了,所以函数命名的时候一定要清楚。
1 | switch (high) |
二、修改一个函数
这里前面一个函数需要修改才能使用,因为实现的功能是读取文件内容,把内容添加到链表后面,不是手动输入,所以前面的addtofile函数需要修改。
修改前:
1 | struct strings *addtofile(struct strings *head)//添加节点 |
先把文件读写的代码加上去FILE *fp=fopen(data,"r");
同样加上是否读取文件成功的判断,gets
改成fscanf(fp,"%s",ne->s)
。
修改后:
1 | struct strings *addinlist(struct strings *head,char* data) |
最后就完成了。
附加:按字母键跳转选项
菜单前面有ABCDEF,其实还可以通过按字母跳转,原理相同,关键在于求出高亮的是第几行。
1 | if (c<='z'&& c>='a')c=c-('a'-'A'); |
完整代码
(运行平台vscode)
1 |
|