6.10 CD唱片应用程序
现在,你已学习完curses函数库提供的功能,下面可以开发一个样本应用程序了。下面的C语言版本的样本程序使用了curses函数库,这使得屏幕显示的信息更加整齐规范,并且它用一个滚动窗口来显示曲目清单。
整个应用程序长达8页,所以我们将其分割为几个部分。完整的源代码curses_app.c可以从Wrox出版社的Web站点上获取。这个程序与本书中的其他程序一样,都遵循GNU公共许可证。
CD数据库应用程序的这个版本使用了前面章节所提供的信息。它源于第2章里的shell脚本程序。我们并未针对C语言实现版本对该程序进行重新设计,所以你还可以从这个版本中看到很多原来shell脚本的特征。要注意的是,这个实现版本还有一些明显的不足,我们将在后面的修订版本中加以解决。
我们将这个应用程序的代码分割为几个部分,并以下面各小节的标题加以说明。这里所使用的代码编排规定与本书的其他部分不太一样,在这里,阴影部分的代码只用于显示对应用程序里其他函数的调用。
6.10.1 新CD唱片应用程序的开始部分
代码的第一部分只用于声明将在后面用到的变量和函数,并初始化一些数据结构:
(1)首先,包含所有必需的头文件,并定义一些全局常量:
(2)现在,需要定义一些全局变量。变量current_cd用于保存正在处理的当前CD唱片的标题。该变量的第一个字符被初始化为空字符null,表示用户还未选择CD唱片。\0并不是绝对必需的,但它能确保该变量被初始化了,而这通常是件好事。变量current_cat用于记录当前CD唱片的分类号。
(3)接下来是一些文件名的声明。为简单起见,这个版本中的文件名都是固定的,临时文件的文件名也是如此。
但如果有两个用户在同一目录下同时运行这个程序,就会出现问题。获得数据库文件名的更好方法是通过程序的参数或是环境变量。我们也需要一种更好的方法来生成一个唯一的临时文件名,这可以通过POSIX的tmpnam函数来完成。我们将在第8章使用MySQL存储数据时解决这些问题。
(4)最后,给出所有函数的原型定义:
(5)在查看这些函数的具体实现之前,你需要定义一些菜单结构(实际上是一个菜单选项的数组)。当一个菜单选项被选中时,其第一个字符将被返回。例如,如果菜单选项是Add New CD,那么当这个选项被选中时,字符a将被返回。当用户选中一张CD唱片后,扩展的菜单选项extended_menu将被显示:
上面的内容结束了程序的初始化过程。接下来,可以开始进入程序中的函数了。但首先,需要了解这些函数之间的相互关系,如图6-9所示,一共有16个函数,分为如下3类:
❑ 绘制菜单
❑ 将CD唱片资料添加到数据库中
❑ 获取和显示CD唱片数据
图 6-9
6.10.2 main函数
main函数允许用户从菜单中进行选择,直到选中quit(退出)为止,如下所示:
下面我们分别对程序中的3类函数进行分析。
6.10.3 建立菜单
本节查看与程序的用户接口相关的3个函数。
(1)main函数调用的getchoice函数是本节的主要函数。getchoice函数的参数有:greet——介绍信息,choices——指向主菜单或扩展菜单(这取决于用户是否选择了一张CD唱片)。你可以在前面的main函数中看到main_menu或extended_menu是如何作为参数传递的:
(2)getchoice函数内部调用了两个函数:clear_all_screen和draw_menu。我们先来看看draw_menu函数:
(3)接下来是clear_all_screen函数,让人惊讶的是,它只是清屏并重写软件标题。如果用户选中了一张CD唱片,则在屏幕上显示它的信息:
6.10.4 操作数据库文件
本节介绍用于添加或更新CD数据库的函数。被main函数调用的函数有:add_record、update_cd和remove_cd。
1.添加记录
(1)添加一张新CD唱片的资料到数据库:
(2)get_string函数的作用是从屏幕当前位置读取一个字符串,并将其末尾可能存在的新行符删除:
(3)get_confirm函数提示并读取用户的确认信息。它读取用户的输入字符串,检查该字符串的第一个字符是否是Y或y,如果是其他字符,则认为用户未确认:
(4)最后,我们来看insert_title函数。它的作用是将标题字符串添加到标题文件的末尾,从而在CD数据库中添加一个标题记录:
2.更新记录
(1)main函数调用的另一个文件操作函数是update_cd。这个函数使用了一个带边框、可卷屏的子窗口,它需要用到一些常量,由于这些常量在后面的list_tracks函数中还会用到,所以这些常量被定义为全局常量。
(2)update_cd函数允许用户重新输入当前CD唱片中的曲目。在删除以前的曲目记录后,它会提示用户输入新资料:
我们将在下面继续列出这个函数的剩余代码。在这里,我们稍作停顿,解释一下如何在带边框的卷屏窗口中输入数据。这里使用的技巧是先创建一个子窗口,围绕它画一个边框,然后在这个带边框的子窗口中再添加一个新的卷屏子窗口。
3.删除记录
(1)main函数调用的最后一个操作数据库的函数是remove_cd:
(2)现在,只需要列出remove_tracks函数。该函数的作用是删除当前CD唱片中的曲目记录。它同时被update_cd函数和remove_cd函数调用:
6.10.5 查询CD数据库
本节介绍如何访问数据,为便于访问,数据被存储在一对平面文件中,并以逗号作为字段的分隔符:
(1)所有收集嗜好的本质都是为了了解你收集的东西数量有多少。下面这个函数就是用来执行这个任务的。它对数据库进行扫描并统计出总的唱片数目和曲目数:
(2)如果不小心将最喜欢的CD唱片的标签弄丢了,不用担心!由于已经将CD唱片的详细信息录入数据库,所以可以通过调用find_cd函数来查找曲目清单。它提示用户输入一个字符串,根据该字符串在数据库中进行匹配检索,并把找到的CD唱片标题放入全局变量current_cd中:
虽然catalog指向的数组比current_cat要大,并且很可能会覆盖内存,但在fgets函数中的检查就不会发生上述问题。
(3)还需要把用户选中的CD唱片中的曲目列在屏幕上。这里会用到在上一小节中为update_cd函数中的子窗口使用所定义的全局常量:
(4)前面两个函数都调用了get_return函数,它的作用是提示用户按下回车键并读取它,其他字符将被忽略:
运行这个程序,你将看到如图6-10所示的输出结果。
图 6-10