(c语言课程设计)C语言文本编辑器

(c语言课程设计)C语言文本编辑器


2024年3月4日发(作者:)

文本编辑器

文本编辑器是最常用的文档创建和编辑工具。随着计算机科学与技术的发展,用来处理文本的编辑器随处可见,并且形式多样。比如,Windows下的记事本,写字板,EditPlus,UltraEdit等都是十分优秀的文本编辑器和处理工具。在本章中,我们将向读者讲解如何利用C语言来发展开发一个简易的文本编辑器。

1 设计目的

利用C语言的单链表数据结构及相关函数,本章编程实现了一个与DOS操作系统下的Edit相似的文本编辑器。再次文本编辑器中,用户可以通过快捷和选择菜单项,完成基本的文本编辑器和文件处理工作。

通过文章的介绍,读者可以了解文本编辑器的开发过程,掌握菜单的开发技巧,加深对文件操作的理解。更重要的是,希望此程序能抛砖引玉,引领读者掌握编程的方法和技巧,开发出更优秀的程序。

2 功能描述

如图.1所示,文本编辑器主要由五大功能模块构成,它们分别是文件操作模块,文本编辑模块,剪切操作模块,菜单控制模块和帮助及其他模块。下面分别简要介绍功能模块的功能。

(1)文件操作模块。在文件操作模块中,主要完成文件的创建,打开,保存和另存操作。用户可以选择File菜单上的New子菜单来完成新建文本文件操作;选择File菜单上的Save子菜单来完成保存文件操作选择File菜单上的Open子菜单来完成打开文件操作;选择Flie菜单上的Save as子菜单来完成文件的另存为操作。在文件的打开,保存和另存为操作中,系统会提示用户输入文件路径及文件名。值得一提的是,当用户打开一个文件时,指定的文件必须存在,否则系统会报错。

(2)文本编辑器模块。在文本编辑器模块中,主要完成在编辑窗口中以添加或插入的方式输入字符,删除光标所在当前位置的单个字符或前一个位置的单个字符,朝上下左右4个方向的光标移动操作。当光标所在位置及后面的位置没有字符时,系统会以添加的方式输入字符;当光标所在位置及后面的位置有字符时,系统会已插入的方式输入字符。用户可以使用BackSpace键删除光标前一个字符,也可以使用Del键删除当前位置的字符或删除Ctrl+左移(右移)键i选定了的多个字符。用户可以使用左移键(←),右移键(→),上移键(↑)和下移键(↓)来移动光标位置。

(3)剪贴板操作模块。在剪贴板操作模块中,主要完成对已选定文本的剪切,复制,粘贴工作。如果用户要剪切文本以便可以将它移动到其他位置,可通过Ctrl+X左移键(右移键)先选定文本,然后选择Edit菜单上的Cut子菜单或按Ctrl+X快捷键来完成剪切任务。如果用户要复制文本以便可以将它黏贴到其他位置,必须先选定文本,然后选择Edit菜单上的Copy紫菜单或按Ctrl+C快捷键来完成复制任务。如果用户要粘贴剪切或复制的文本,必须将光标置于要粘贴文本的位置,然后选择Edit菜单上的Paste子菜单或按Ctrl+V快捷键来完成粘贴任务。

(4)菜单控制模块。在菜单控制模块中,主要完成菜单的显示。光带条在子菜单之间的上下移动或菜单之间的左右移动和子菜单项的选取。本文本编辑器共有Flie,Edit和Help3个子菜单项,用户可以分别按F1,F2和F3功能键来完成这3个菜单项的调用,即显示某项菜单。用户可按光标上移或下移键在某菜单项的子菜单之间循环移动,也可使用光标的左移

或右移键在3个菜单项之间循环移动。当光带移动到某个字菜单项上时,用户此时可使用Enter键来选取相关菜单选项。

(5)帮助及其他模块。在帮助及其他模块中,主要完成系统功能及按键的简要介绍。其他模块包括文本的快速预览和窗口的显示。用户可按F10功能键来打开快速预览窗口,在快速预览窗口中没有功能菜单条。主窗口要有菜单栏,文本编辑区和状态栏三大部分构成,菜单栏用来显示菜单项,文本编辑区主要用来文本字符的输入,删除等操作,状态栏主要用来显示当前光标在文本窗口中的坐标值。

注意:Turbo C2.默认定义的文本窗口为整个屏幕,共有80列(或40列),25行的文本单元,每个单元包括一个字符和一个属性,字符即ASCII码字符,属性规定该字符的颜色和强度。同时,他还规定整个屏幕的左上角坐标为(1,1),右下角坐标为(80,25)。并规定沿水平方向为X轴,方向朝右;眼垂直方向为Y轴,方向朝下。

3 总体设计

3.1 功能模块设计

在.2节中,简单描述了各功能模块的作用,下面分别介绍各功能模块的具体设计。在介绍各功能模块的具体设计之前,有必要先描述一下主程序的执行流程。

1. 程序执行主流程

文本编辑器程序执行主流程如图.2所示,它是在main()函数中实现的。他首先初始化一些全局变量及结构数组,接着调用drawmain()函数来显示主窗口,然后调用while(1)进入主循环,等待用户按键,最后程序根据用户的按键值,进行相应的处理,完成文本编辑的相关工作。下面对图.2中的按键判断和相关处理作补充说明。

(1)若按键为常规字符,即其ASCII码大于32小于127,则继续判断在文本编辑区的当前光标位置有没有字符,若有字符,则调用insert()函数,将此字符插入在当前位置,否则在判断没有满行后,将此字符添加在单链表的数据域中,若此行已满,则执行添加新行操作。

(2)若按键为Enter键,则将光标移至下一行的行首,等待用户输入新的字符。

(3)若按键为光标移动键(左,右,上,下)且移动后的位置满足相关条件,则执行gotoxy()操作,将光标移动至目标位置。

(4) 若按键为BackSpace键,则将调用Del()函数将光标的前一个字符从单链表中删除;若按键为Del键,也将调用del()函数将光标的当前位置的字符从单链表中删除。

(5)若按键为Ctrl开头的按键,则执行与其相关的操作。具体来说,若为Ctrl+左移键(←),则将选定当前光标的位置开始向右的一个字符,若按住Ctrl键不放,连续按右移键,可以选定多个字符。若为Ctrl+左移键(←),则将执行与以上相同的操作。若为Ctrl+X键,则将选定相关内容保存起来,且从单链表中删除选定的字符后重新显示单链表的内容。若为Ctrl+C键,则将选定的相关内容保存起来,重新显示单链表中的内容(目的:为了去除字符的底色)。若为Ctrl+V键,则调用insert()函数将保存起来的字符插入在单链表中,并重新显示单链表中的内容。

(6)若按键为F10键,则调用qview()函数,实现文本的快速预览。

若按键为F1,F2,F3功能键,则调用menuctrl()菜单控制函数,在此函数中完成案件的具体判断和执行相应功能操作。若为F1键,则调用File菜单;若为F2键,则调用Edit菜单;若为F3键,则调用Help菜单。

2.文件操作模块

在此模块中,主要实现文件的新建、打开、保存和另存为操作。在此系统中,文件的新建操作实现比较简单,文件另存为操作与保存操作类似,下面重点介绍在此文本编辑器程序中,文件的打开和保存操作的具体设计和实现。在介绍之前,我们先简单描述一下程序中用到的保存数据的数据结构。在此程序中,共有两种类型的单链表,我们称其为行单链表和列单链表,一个列单链表用来保存一行的字符,有多少行即有多少个这样的单链表。行单链表只有一个,它的每个节点的数据域用来保存不同列单链表的首节点的地址。例如,第4行第4列的字符保存在行单链表的第4个节点的数据域所指的列单链表的第4个节点的数据域中。有关具体数据结构的定义,在后面的小节中会有具体介绍。

1)打开文件

文件的打开流程如图.3所示,它首先提示用户输入要打开文件的文件名,若该文件不存在或由于其他原因打开失败,则会结束文件打开操作。若文件成功打开并且文件指针没有到文件尾,则从文件中一次读取一个字符,并将该字符添加到一列单链表节点中,直至遇到换行符(ASCII码10)或连续读取字符个数大于76(在此文件编辑器中,每行最多为76个字符)。当列单链表形成后,它的首地址将被保存至行单链表的相应节点的数据域中,如此动作,直至文件指针指向文件尾部而结束。

注意:由于本程序中每行以回车符(ASCII码为13)结束,而当用Windows的记事本创建一个文本文件,打开此文件并用fgetc()函数读取时,程序写入列单链表节点中的值是ASCII码为13的回车符。

2)保存文件

保存文件操作主要完成将单链表中的数据写入文件中的任务,它的具体实现流程如下。

(1) 用户输入一个保存此单链表数据的文件名。

(2) 以只写方式打开此文件,若成功打开此文件,则执行步骤(3);否则退出。

(3) 读取行单链表中的节点数据域的值,若值不为空,则执行步骤(4);否则执行步骤(6)。

(4) 依次读取行单链表节点中保存的首地址的相应列单链表节点的数据域的值,若其值为回车符,则用换行符替代后将其写入文件中;否则直接将其值写入文件中,直至该列单链表中指针域为NULL的最后一个元素结束。

(5) 读取行单链表中的下一个节点,并跳至步骤(3)。

(6) 关闭文件,退出。

3.文件编辑模块

在文件编辑模块中,主要完成以添加或插入的方式输入字符、删除光标所在的当前位置或前一个位置的单个字符、朝上下左右4个方向的光标的移动操作。下面介绍这4个功能的具体设计与实现。

1) 添加字符

当光标处在文本编辑的最后一行的位置且光标后面没有字符时,若此时输入字符,程序会判断一行中字符的个数,若字符个数不等于76,则在当前的列单链表的最后一个节点中保存输入的字符,然后添加一个新的节点来保存下一个输入的字符:若等于76,则在当前的列单链表的最后一个节点中保存输入的字符,然后在行单链表中添加一个新节点用来保存下一行的列单链表的首地址,添加一个新的列单链表节点来保存下一个用户输入的字符。

2) 插入字符

若光标所在处已经存在字符,当用户在当前位置输入字符时,程序会调用insert()函数将输入的字符在光标所在的位置处在列单链表中插入,插入完成后,会调用test()函数来检查各行是否满足只有76个字符的条件,若不满足此条件,则在此函数中会对多出的字符进行处理。下面分别对列单链表中字符的插入过程和单链表的检查过程进行介绍。

若在第m行,第n列的位置插入一个字符,其insert()过程描述如下:

(1) 定位至行单链表中的第m个节点,得到这个节点的数据域的值,其值为对应列单链表中第一个节点的地址。

(2) 定位至列单链表中的第n-1个节点。

(3) 创建一个新的列单链表节点,用其数据域保存输入的字符。

(4) 若字符插入在第m行第1列,则直接将行单链表中第m个节点的数据域的值改变为新的列单链表节点的地址,新的列单链表节点的指针域指向列单链表中原来的第1个节点。若字符不是插入在第m行第1列,则执行简单的单链表中插入节点的操作。

(5) 插入此字符后,调用test()函数,从第m行开始检查各行是否满足每行只允许有76个字符的条件,若不满足此条件,则必须进行处理。

其test()检查处理过程描述如下:

(1) 用指针tail指向已经插入了新节点的列单链表中的最后一个节点。

(2) 若此单链表中节点数超过76个,则指针p1会指向此列单链表中的第76个节点,指针p2指向第77个节点,并将p1所指节点的指针域设置为NULL。

(3) 若tail所指节点的数据域为Enter键(ASCII为13)且在行单链表中只有m个节点,则在此行单链表中添加一个新的节点,新节点的数据域为p2的值,指针域为空,并将m节点的指针域指向它;若tail所指节点反而数据域为Enter键(ASCII为13)且在行单链表中有多于m个节点,与上面不同的是,它执行的是在行单链表插入一个新的节点的操作。

(4) 若tail所指节点的数据域不是回车符,p1的数据域为回车符并且行单链表中只有m个节点,则在行单链表中添加一个新的节点,新节点的数据域为p2的值,指针域为空,并将第m节点的指针域指向它;若tail所指节点的数据域不为回车符并且行单链表中有多于m节点,则将tail的指针域指向行单链表中第m+1个节点所指的列单链表的首节点,并将行单链表中第m+1个节点的数据域修改成指针p2的值,并对行单链表中第m+1个节点所指的列单链表进行test()检查,相似的处理过程至行单链表中的最后一个节点结束。

3) 删除字符

当用户按下Del键时,系统会调用del()函数在单链表中删除当前光标所在处的字符;当用户按下BackSpace键时,系统也会调用这个函数在单链表中删除当前光标所在处前一个位置的字符。

若在第m行、第n列的位置删除一个字符,其在列单链表中删除一个节点的操作域插入工作十分相似,所以这里重点介绍删除该字符后,单链表中数据的前移工作过程。

(1) 在相应的列单链表中删除第n个节点。

(2) 判断第m行是否存在并且判断在此行中是否有字符,若第m行不存在,或此行中没有字符,则结束字符删除过程,否则执行步骤(3)。

(3) 用tail保存第m行相应列单链表中最后一个节点的地址,并将最后一个节点的指针域保存为第m+1行中相应列单链表的第一个元素的地址。

(4) 计算出第m行中没有字符的位置的个数num,然后在第m+1行的相应列单链表中截取num个节点,并将行单链表中的第m+1节点的数据域改为相应列单链表中的第num个节点后的节点的地址。

(5) 调用m++语句,是变量m增1,跳至步骤(3),开始对下一行进行处理。

4) 移动光标

移动光标的操作主要利用gotoxy()函数来实现,过程非常简单,只需对当前的光标位置和移动方向进行判断后,即可执行gotoxy()过程。例如,如果当前光标在第m行第1列,且按下

了光标左移键,只需将光标移至第m-1行,第76列。

4.剪贴板操作模块

在剪切板操作模块中,主要完成对已选定文本的剪切、复制和粘贴工作,因此剪贴板操作与文本选定工作密切相关。下面分别介绍文本的选定和对选定文本的剪切、复制和粘贴操作的具体实现。

1) 选定文本

用户可按Ctrl+<-或Ctrl+->来选定文本,就具体实现而言,两者基本相同。在介绍选定文本的实现过程之前,先简要介绍一个全局的结构数组r[],它的元素的类型为record结构体类型,每一个元素可保存一个字符的x坐标、y坐标和字符值。其文本选定的实现过程如下:

(1) 当用户按下Ctrl+<-或Ctrl+->键时,程序将当前光标位置向左或向右移动一个位置。

(2) 当前光标所在位置的字符的x坐标、y坐标和字符值保存在数组元素r[value]中value从0开始,若按减为Ctrl+<-,value值在原来基础上每次加1;若按键为Ctrl+->,value值在原来基础上每次减1.

(3) 调用Colorview()函数,用不同的颜色来显示已经选定的当前文本,以达到突出选定文本的效果。

2) 剪切

用户可按Ctrl+X键或通过Edit菜单选项来剪切选定的文本,若之前没有选定的文本,此按键无效。它的实现过程如下:

(1)若全局变量value的值大于0(大于0表示已经有文本选定),则执行下面操作。

(7)

(2)保存当前位置的坐标,利用循环语句,依次利用x[0]至x[value-1]数组元素保存已选定字符的坐标,调用del()函数在单链表中一次删除一个选定的字符。

(3)利用全局变量backup保存value的值后,将value赋值为0。

(4)重新在文本编辑器中显示单链表中保存的所有字符,并将光标置位到原来的位置。

3)复制

用户可按Ctrl+C键或通过Edit菜单选项来复制选定的文本,复制操作的实现比剪切操作简单,它的实现过程如下:

(1) 保存当前位置的坐标。

(2) 利用全局变量backup保存value的值后,将value赋值为0。

(3) 重新在文本编辑器中显示单链表中保存的所有字符,并将光标置位到原来的位置。

4)粘贴

用户可按Ctrl+V键或通过Edit菜单选项,完成粘贴操作。这一操作必须在剪切或复制操作之后出现。它的具体实现过程如下:

(1) 若全局变量backup的值大于0(大于0表示已经有字符放入了剪贴板数组)中,则执行下面的操作。

(2) 保存当前位置的坐标,利用循环语句,依次利用x[0]至x[backup-1]数组元素保存已选定字符的坐标和字符值,调用insert()函数在单链表中一次插入一个字符。

(3) 重新在文本编辑器中显示单链表中保存的所有字符,并将光标置位到原来的位置。

5.菜单控制模块

在菜单控制模块中,主要完成菜单的显示、光带条在子菜单之间的上下移动或菜单之间的左右移动以及子菜单项的选项工作。下面分别介绍这3项功能的具体实现。

1) 显示菜单

用户可按F1、F2和F3功能键来分别调用File、Edit和Help菜单,即完成菜单的显示。当

按下这3个功能键中的某个功能键时,程序会调用menuctrl()函数来完成菜单的调用操作。在menuctrl()函数中,会根据功能键的键值调用drawmenu(value,flag)函数,参数value、flag都为局部变量,分别用来保存调用某个菜单、某个菜单下的第几个菜单选项。例如,按F1键后,它的默认值为drawmenu(0,0),表示绘制File菜单及其5个菜单选项,并将菜单选择光带条置于第一个菜单选项上。下面简要描述一下draw(value,flag)函数的过程。

(1) 先取value除以3的余数m(因为有3个菜单项,所以除数选择3),根据m的值来绘制不同的菜单。m的取值为0、1、2、。当m等于0时,表示绘制File菜单;其余类推。

(2) 然后绘制菜单的边框及菜单选项值。

(3) 取File除以x的余数t,x的取值视m的取值而定,如当m=5时,x=5,因为File菜单下有5个选项。

(4) 根据t的值,用不同的前景色和背景色在原来的位置重新显示菜单选项,以达到光带条的效果。

2) 移动菜单光带条

当用户按F1、F2和F3中的某个功能键调用了某个菜单后,可继续按光标左移、右移、上移和下移键来实现菜单之间的切换和菜单选项之间的切换。

(1) 若为左移键,则调用drawmenu(--value,flag)函数,将切换至某个菜单的左边邻居菜单。若当前菜单为最左边的File菜单,则切换至最右边的Help菜单。若为右移键,则调用drawmenu(++value,flag)函数。

(2) 若为上移键,则调用drawmenu(value,--flag)函数;若为下移键,则调用drawmenu(value,++flag)函数。

3) 选取菜单

当用户将光带选择条置于某个菜单选项上时,可按Enter键来选取该菜单选项。选取菜单操作的实现比较简单,它主要利用a=(value%3)*10+flag%b来计算出选择的菜单选项的编号。选取不同菜单选项后,a的值不同。这样,程序可根据a的值,返回给main()函数不同的标记,在main()函数中,可根据标记的不同来执行相关功能。

6.帮助及其他模块

帮助模块主要用于提示用户如何使用本软件,它的实现非常简单。同样,文本的快速预览模块是在原来主窗口显示模块的基础上,去除了菜单的显示。

主窗口主要由菜单栏、文本编辑区和状态栏3大部分构成。菜单栏用来显示菜单项,文本编辑区主要用来完成文本字符的输入、删除等操作,状态栏主要用来显示当前光标在文本窗口中的坐标值。它主要利用文本窗口的gotoxy()函数和cprintf()函数来实现,这里需要对文本窗口的坐标进行仔细设计。

.3.2数据结构设计

本程序定义了3个结构体,分别与剪贴板、列单链表和行单链表相关。下面分别介绍这3个结构体及几个全局变量。

1. 与剪贴板相关的record结构体

typedef struct record

{

char ch;

int col,line;

}record;

record结构体表示一个字符所具有的属性,当用户使用相关按键选定文本后,选定的文本保存在record结构体类型的数组中。结构体中各字段表示的意义如下。

char ch:保存一个选定的文本字符。

int col,line:分别保存选定字符所在位置的x轴坐标和y轴坐标。

2. 与列单链表相关的node结构体

typedef struct node

{

char ch;

struct node *next;

}node;

node结构体定义了在一个单链表中保存行中的单个字符的结构,我们称由node类型的节点构成的单链表为列单链表。结构体中各字段表示的意义如下。

char ch:数据域,保存一个字符。

struct node*next:指针域,指向列单链表中的下一个节点。

3. 与行单链表相关的Hnode结构体

typedef struct Hnode

{

node *next;

struct Hnode *next1;

}record;

Hnode结构体定义了在一个单链表中保存列单链表首节点地址的结构,我们称由Hnode类型的节点构成的单链表为行单链表。结构体中各字段表示的意义如下。

node *next:数据域,。指向列单链表的首节点的地址

struct Hnode*next1:指针域,指向列单链表中的下一个节点。

4. 全局变量及数组

int value,backup,NUM:value保存有值数组元素的最大下标值,backup保存value的副本,NUM保存当前行中用户输入的字符个数。

record r[500]:定义一个有500个元素的结构体数组,每个数组元素可保存一个选定的文本字符及其坐标值。

.3.3函数功能描述

1)drawmain()

函数原型:void drawmain()

drawmain()函数用于在程序中会只包括菜单栏,编辑区和状态栏在内的主窗口。

2)qview()

函数原型:void qview(Hnode*q)

qview()函数用于快速预览文本。q为指向行单链表中第一个节点的指针。

3)view()

函数原型:viod view(Hnode*q)

view()函数用于按行显示保存在单链表中的文本字符,q为指向行单链表中第一个节点的指针。

4)check()

函数原型:int check(Hnode*Hhead,int m,int n)

函数用于在单链表中检查第m行,第n列位置的字符,若为常规字符,则返回该字符;否

则返回0或-1.

5)judge()

函数原型:int judje(Hnode*Hhead,int m)

judge()函数用于返回第m行中不包括回车符在内的常规字符的个数。

6)del()

函数原型:int del(Hnode*Hhead,int m,int n)

del()函数用于在单链表中删除第m行,第n列位置的字符。

7)test()

函数原型:int test(Hnode*Hhead,int n)

test()函数用于执行后,检验第n行及后面的数据,使其满足每行不多于76个字符的规则。

8)insert()

函数原型:viod insert(Hnode*Hheadint m,int n,char a)

insert()函数用于在第m行,第n列位置的前一个位置插入单个字符。

9)control()

函数原型:void control(int A,Hnode*Hhead)

control()函数用于对左移键(右移键)进行响应,A为按键的整数值,Hhead为行单链表的首地址。

10)colorview()

函数原型:voi colorview(Hnode*Hhead,int x,int y)

colorview()函数用于用不同的前、背景色现实选择的字符。

11)drawmenu()

函数原型:void drawmenu(int m,int n)

drawmenu函数用于画菜单,m表示第几项菜单,n表示第m项的第n个子菜单项。

12)menuctrl()

函数原型:int menuctrl(Hnode*Hhead,int A)

menuctrl()函数用于菜单控制。

13)save()

函数原型:void save(Hnode*head)

save()函数用于将head所指的行单链表中所指的各个列单链表中的数据域的值写入文件,文件路径和文件名由用户指定。

14)saveas()

函数原型:void saveas(Hnode*head)

saveas()函数用于实现文件另存工作,文件路径和文件名由用户指定。

15)opens()

函数原型:void opens(Hnode*Hp)

opens()函数用于从任意文本文件中读取文件内容,保存至行单链表形式的数据结构中。

)main()

函数原型:void main()

main()函数为程序的主控函数,对它的描述可参见.3.1小节。

.4程序实现

.4.1 源码分析

1.程序预处理

程序预处理包括头文件的加载,以及结构体,常量和全局变量的定义。

/*文本编辑器editor源代码*/

#include

#include

#include

#include

#define LEFT 0x4b00 /*←:光标左移*/

#define RIGHT 0x4d00 /*→:光标右移*/

#define DOWN 0x5000 /*↓键:光标下移*/

#define UP 0x4800 /*↑键:光标上移*/

#define ESC 0x011b /*ESC键:取消菜单打开操作*/

#define ENTER 0x1c0d /*回车键:换行*/

#define DEL 21248 /*DEL键:删除当前字符*/

#define BACK 3592 /*BackSpace键:删除当前光标位置前一个字符*/

#define CL 29440 /*ctrl+←键:从右至左,选定文本*/

#define CR 29696 /*ctrl+→键:从左到右,选定文本*/

#define Cc 11779 /*ctrl+c键:将选定文本,复制一份到剪贴板中*/

#define Cv 12054 /*ctrl+v键:将剪贴板中的内容复制到当前位置*/

#define Cx 11544 /*ctrl+x键:对选定文本,执行剪切操作*/

#define F1 15104 /*F1键:打开文件菜单*/

#define F2 15360 /*F2键:打开编辑菜单*/

#define F3 156 /*F3键:打开帮助菜单*/

#define F10 17408 /*F10键:进入文本快速预览模式*/

int value,backup,NUM;

/*value保存有值数组元素的最大下标值,backup保存value的副本,NUM保存当前行中的用户输入的字符个数*/

typedef struct record

{

char ch; /*保存一字符*/

int col, line; /*x轴和y轴坐标*/

}record;

record r[500]; /*定义一个有500个元素的结构体数组,保存选定的文本字符的属性*/

typedef struct node /*定义保存行中的单个字符的结构*/

{

char ch; /*数据域:保存一字符*/

struct node *next; /*指针域:指向下一个结点的指针*/

}node;/*由此类型节点构成的单链表,命名为:列单链表*/

typedef struct Hnode /*定义保存所有列单链表首节点的指针的结构*/

{

node *next; /*指向列单链表的首节点的地址*/

struct Hnode *nextl; /*指向下一个节点的指针*/

}Hnode;/*由此类型节点构成的单链表,命名为:行单链表*/

2.绘制主窗口

绘制文本编辑器主窗口由drawmain()函数来完成,通过准确定位相关输出对象的坐标来完成主窗口的绘制。主窗口共分为3个区域:菜单区,文本编辑去和状态栏区。

void drawmain() /*画主窗口函数*/

{

int i,j;

gotoxy(1,1); /*在文本窗口中设置光标至(1,1)处*/

textbackground(7); /*选择新的文本背景颜色,7为LIGHTGRAY淡灰色*/

textcolor(0); /*在文本模式中选择新的字符颜色0为BLACK黑*/

insline(); /*在文本窗口的(1,1)位置处中插入一个空行*/

for(i=1;i<=24;i++)

{

gotoxy(1,1+i); /*(x,y)中x不变,y++*/

cprintf("%c",196); /*在窗口左边输出-,即画出主窗口的左边界 */

gotoxy(80,1+i);

cprintf("%c",196); /*在窗口右边,输出-,即画出主窗口的右边界*/

}

for(i=1;i<=79;i++)

{

gotoxy(1+i,2); /*在第2行,第2列开始*/

cprintf("%c",196); /*在窗口顶端,输出-*/

gotoxy(1+i,25); /*在第25行,第2列开始*/

cprintf("%c",196); /*在窗口底端,输出-*/

}

gotoxy(1,1); cprintf("%c",196); /*在窗口左上角,输出-*/

gotoxy(1,24); cprintf("%c",196); /*在窗口左下角,输出-*/

gotoxy(80,1); cprintf("%c",196); /*在窗口右上角,输出-*/

gotoxy(80,24); cprintf("%c",196); /*在窗口右下角,输出-*/

gotoxy(7,1); cprintf("%c %c File %c %c",179,17,,179); /* | < > |*/

gotoxy(27,1); cprintf("%c %c Edit %c %c",179,17,,179); /* | < > |*/

gotoxy(47,1); cprintf("%c %c Help %c %c",179,17,,179); /* | < > |*/

gotoxy(5,25); /*跳至窗口底端*/

textcolor(1);

cprintf(" Row:1 Col:1");

gotoxy(68,25);

cprintf("Version 2.0");

}

3.文本字符显示输出

文本字符显示输出模块的作用是通过循环读取各单链表,酱爆错在单链表众多的字符在文本

编辑区中显示输出。

(1) 通过qview(hnode*q)函数,可实现文本字符的快速预览。

(2) 通过view(honde*q)函数,可实现文本字符在编辑区域的显示。

void qview(Hnode *q) /*快速预览文本:开头:#,回车:* */

{

void view(Hnode *q); /*view()函数声明*/

node *p;

int i;

window(1,1,80,25); /*定义文本窗口大小*/

clrscr(); /*清屏*/

/*循环读取两个单链表中的值:q是一个指向行单链表首节点的指针,

此单链表数据域的值为实际保存各行字符的列单链表p中的首节点地址*/

do{

p=q->next; /*p指向保存行数据的列单链表的首节点的地址*/

cprintf("#"); /*每行开头,打印此字符,不管前面是否有回车符*/

while(p!=NULL) /*循环读取单链表p中的值*/

{

if(p->ch==13) putch('*'); /*若为回车键,打印出*号*/

else

putch(p->ch); /*输出各行中的字符到预览窗口*/

p=p->next; /*指向下一个节点*/

}

q=q->nextl; /*指向下一个节点*/

printf("n");/*输出一个回车*/

}while(q!=NULL);

getch();

clrscr();

drawmain();/*按任意键后,回到主窗口界面*/

window(2,2,79,23);

textbackground(9);

for(i=0;i<24;i++)

insline(); /*插入24个空行*/

window(3,3,78,23);

textcolor(10);

}

void view(Hnode *q) /*按行显示保存在单链表中的文本字符,q为指向行单链表中第一个节点的指针*/

{

node *p; /*p为保存列单链表节点元素地址的指针*/

clrscr(); /*清屏*/

/*双重循环,读取并显示保存在单链表中字符*/

do{

p=q->next;

while(p!=NULL&&p->ch>=32&&p->ch<127&&p->ch!=13&&p->ch!=-1) /*指针p不能为空,且数据域必须为常规字符*/

{

putch(p->ch);/*在文本窗口中输出该字符*/

p=p->next; /*指向下一个节点*/

}

q=q->nextl; /*指向下一个节点*/

if((p->ch==13||p->ch==-1)&&q!=NULL) gotoxy(1,wherey()+1); /*若ch为回车或EOF标记,光标跳至下行的开始处*/

}while(q!=NULL); /*逐行逐列显示文本字符*/

}

4.删除字符

程序调用del(Honde*Hhead,int m,int n)函数来完成删除第行、第列位置的字符。它的具体过程在功能模块设计部分已经详细介绍。下面介绍另外两个对字符进行检测的函数,它在字符的删除、插入等许多操作中都有用到。

(1) 调用check(Hnode*Head,int m,int n)函数,在单链表中检查第m行、第n列位置的字符,若为常规字符,则返回该字符;否则返回0或-1.,

(2) 调用judge(Hnode*Hhead,int m)函数,在单链表中统计第m行中的常规字符的总个数,并返回统计值。

int del(Hnode *Hhead,int m,int n) /*del():删除第m行,第n列位置的字符*/

{

Hnode *q,*q1;

node *p1,*p2,*tail;

int i,num=0,j,flag=0;

q=Hhead;

if(n==0&&m==1) return; /*第1行,第0列不存在*/

if(n==0&&m>1) /*若为第0列字符,但行必须大于1,执行向上行移处理*/

{

n=76;

m=m-1;

gotoxy(n,m);/*移至第m-1行,第76列*/

flag=1; /*移位的标志置1*/

}

for(i=1;i

q=q->nextl;

p1=q->next;

for(i=1;i

p1=p1->next;

p2=p1->next; /*p2指向列单链表中的第n个元素*/

if(n==1) /*若是删除第m行第1列的字符*/

{

q->next=p1->next;

free(p1);

}

else

{

p1->next=p2->next; /*在单链表中删除第m行第n列的元素*/

free(p2);

}

/*删除掉第m行第n列的元素后,处理行单链表中第m个节点后的数据向前移的任务*/

while((num=judge(Hhead,m++))>0) /*执行一次judge(Head,m)后,m才加1.这里必须满足行常规字符数不为0的条件*/

{

p1=q->next; q1=q;

if(p1!=NULL) /*若当前行非空*/

{

while(p1->next!=NULL)

p1=p1->next;

tail=p1;/*tail保存列单链表最后一个元素的地址*/

q=q->nextl; /*指向下一行的元素的地址*/

p1=p2=q->next;

tail->next=p1; /*tail的指针域指向下一行的第一个元素的地址*/

}

else /*若当前行的字符个数为0,即删除该字符后,只剩下回车符,则将下一个行单链表中节点的数据域移至前一下节点的数据域*/

{

q=q->nextl; p1=p2=q->next;

q1->next=p1;/*q1->next指向下一行的第一个元素的地址*/

}

for(i=0;i<76-num;i++)

/*当前行还有76-num个空位没有字符,在下一行的单链表中读取字符,直至遇到回车符为止*/

{

p1=p2; /*p1指向p2的前一个节点,p2指向行单链表中下一个节点*/

p2=p2->next;

if(p2->ch==13) break; /*若为回车,跳出循环*/

}

q->next=p2; /*在列单链表中去掉移至上行的元素*/

p1->next=NULL;/*下行移至上行的最后一个元素,指针置空*/

}

return flag; /*返回0:表示没有换位,返回1:表示有换位*/

}

int check(Hnode *Hhead,int m,int n) /*check():在单链表中检查第m行第n列位置的字符,若为常规字符,则返回该字符*/

{

int i;

Hnode *q;

node *p;

q=Hhead;

for(i=1;i

q=q->nextl;

p=q->next;/*获取第m个节点的数据域*/

for(i=1;i

p=p->next;

if(p->ch==13) return -1; /*若第m行,第n列的字符为回车键,则返回-1*/

if(p->ch>=32&&p->ch<127) return p->ch; /*若第m行,第n列的字符为常规字符,则返回该字符*/

else return 0; /*若第m行,第n列的字符既非回车符又非常规字符,则返回0*/

}

int judge(Hnode *Hhead,int m) /*judge():返回第m行中的常规字符总的个数,不包括回车符*/

{

Hnode *q;

node *p;

int i,num=0;

q=Hhead;

for(i=1;i

q=q->nextl;

if(q==NULL) return -1; /*返回-1,表示第m行不存在*/

p=q->next;

while(p->next!=NULL)

{

p=p->next;

num++; /*统计第m行的字符个数*/

}

/*行尾字符还没有判断,接下来判断行尾字符*/

if(p->ch==13&&num==0) return 0; /*返回0,表示当前行只有一个回车字符*/

if(p->ch>=32&&p->ch<127) return num+1; /*返回num+1,表示当前行的最后一个字符为常规字符*/

if(p->ch==13&&num!=0) return num; /*返回num,表示当前行的最后一个字符为回车符,不计算在内*/

else return 1;/*返回num,表示当前行中只有一个字符,且没有回车符*/

}

5.插入字符

在文本编辑区中插入字符的工作由insert(Hnode,*Hhead,int m,int n,char a)函数和test(Hnode*Hhead,int n)函数配合完成。

(1) 通过insert(Hnode*Hhead,int m,int n,char a)函数,在第m行、第n列的位置之前的一个位置,在单链表中插入字符a。

(2) 通过test(Hnode*hhead,int n)函数,检查和处理第n行及后面的数据,使其满足每行不超过76个字符的规则void insert(Hnode *Hhead,int m,int n, char a) /*第m行,第n列的位置之前一个位置,插入单字符*/

{

int i;

Hnode *q;

node *p,*p1,*p2;

q=Hhead;

for(i=1;i

q=q->nextl;

p1=q->next;

for(i=1;i

p1=p1->next;

p=(node *)malloc(sizeof(node)); /*创建一个新的列单链表节点*/

p->ch=a; /*给此节点的数据域赋值*/

if(n==1) /*插入之前,若只有一个字符在行中,则插在此节点之前*/

{

p->next=q->next;

q->next=p;

}

else

{

p->next=p1->next; /*在第m行,第n列的字符前,插入一字符*/

p1->next=p;

}

test(Hhead,m); /*在插入新元素后,检验并处理单链表中第m行开始的元素,使其满足规则*/

}

/*执行insert()后,检验第n行及后面的数据,使其满足规则*/

int test(Hnode *Hhead,int n)

{

int i=0,num1=1;

node *p1,*p2,*tail,*temp1,*temp2;

Hnode *q;

q=Hhead;

for(i=1;i

q=q->nextl;

tail=p1=q->next;

if(p1==NULL) return; /*若此行没有任何字符,则返回*/

while(tail->next!=NULL) /*定位至列单链表中的最后一个元素*/

tail=tail->next;

/*若此单链表中没有回车符且有超过76个节点时,则p1会指向此列单链表中的第76个节点*/

for(i=0;i<75;i++)

{

if(p1->ch==13||p1->next==NULL) break;

p1=p1->next;

}

p2=p1->next;

p1->next=NULL; /*在此行的最后一个字符的前一个字符处断行,因为插入在此行插入了一个新的字符*/

if(tail->ch!=13) /*若此行行尾不是回车键*/

{

if(p1->ch==13&&q->nextl==NULL)/*若p1的数据域为回车符且行单链表中只有n个节点*/

{

q->nextl=(Hnode *)malloc(sizeof(Hnode)); /*新建一个行单链表节点,相当于添加一个新行*/

q->nextl->nextl=NULL;

tail->next=(node *)malloc(sizeof(node));/*在tail所指节点位置开始继续准备添加字符*/

tail->next->ch=13; tail->next->next=NULL;

q->nextl->next=p2; /*新行单链表节点保存此行多出的字符*/

}

else /*若此行行尾和行中都没有回车键,或者q->nextl不为空*/

{

q=q->nextl;/*q->nextl有可能为空*/

tail->next=q->next;/*将多出的字符与下一行的字符相连*/

q->next=p2;/**/

if(q!=NULL) test(Hhead,++n); /*若行单链表第n个节点后还有节点,继续test()的相同处理*/

}

}

else /*若此列单链表最后一个元素为回车符*/

{

temp2=p2; /*p2指向第77个字符,或者为空(为空表示此行插入一个字符后,没有超出范围*/

while(q!=NULL&&p2!=NULL) /*q指向行列表中的第n个节点.条件:行单链表中第n个节点存中且有第77个字符*/

{ /*条件:在行单链表中只有n个节点,且字符超过了一行规定的76个,且num1标志为1*/

if((q->nextl==NULL)&&(p1!=tail||p2!=NULL)&&(num1==1))

{

num1++;

q->nextl=(Hnode *)malloc(sizeof(Hnode)); /*新建一个行单链表节点,准备存储此行中多出的字符*/

q->nextl->nextl=NULL; q->nextl->next=NULL; /*初始化值*/

}

/*行单链表中第n+1个节点已经存在,下面为在行单链表中插入一个新的节点*/

q=q->nextl; /*q指向行列表中的第n+1个节点*/

temp1=q->next;

q->next=temp2; /*q的数据域为此行中多出的字符所在的列单链表中的节点地址*/

temp2=temp1;

}

}

}

6.选定文本

在文本编辑区选定文本的工作由control(int A,Hnode*Hhead)函数和colorview(Hnode*Hhead,int x,int y)函数配合完成。

(1) 通过control(int A,Hnode*Hhead)函数,对控制键进行响应,移动光标后并将光标所在位置的字符保存在数组中。

(2) 通过colorview(Hnode*Hhead,int x,int y)函数,用不同的颜色显示选定的文本。

/*对控制键进行响应,A:按键的整数值,Hhead:行单链表的首地址*/

void control(int A, Hnode *Hhead)

{

void colorview(Hnode *,int,int); /*函数声明*/

int x,y,flag=0;

x=wherex(); y=wherey(); /*得到当前光标的坐标值*/

if((A==CL)&&(x!=1)) /*ctrl+←,当前光标不是在行首,光标移动*/

gotoxy(wherex()-1,wherey());

if((A==CL)&&(x==1)) /*ctrl+←,在行首*/

gotoxy(abs(judge(Hhead,wherey()-1)),wherey()-1); /*judge(Hhead,wherey()-1)上一行的字符个数作为x值,光标移动*/

if((A==CR)&&check(Hhead,wherey(),wherex())>0) /*ctrl+→,当前光标的右边有字符,光标移动*/

{ flag=1; gotoxy(wherex()+1,wherey()); }

if((A==CR)&&check(Hhead,wherey()+1,1)>0&&check(Hhead,y,x)==0) /*ctrl+→,当前光标处没有字符但下一行的第一列有字符,光标移动*/

{ flag=1; gotoxy(1,wherey()+1); }

if((A==CR)&&x==76) /*ctrl+→,当前光标在当前行的行尾,光标移动*/

{ flag=1; gotoxy(1,wherey()+1); }

if(A==CR&&flag==1) /*ctrl+→,光标已经跳至新处,将当前光标所在位置的字符的坐标和值保存在r数组中*/

{

r[abs(value)].col=wherex();

r[abs(value)].line=wherey();

r[abs(value)].ch=check(Hhead,r[abs(value)].line,r[abs(value)].col);

if(r[abs(value)].ch==-1) r[abs(value)].ch=13; /*若第line行,第col列的字符为回车键,则返回-1*/

value--;

}

if(A==CL&&(x!=1||y!=1)) /*ctrl+←,当前光标并不在窗口左上角,将当前光标所在位置的字符的坐标和值保存在r数组中*/

{

r[abs(value)].col=wherex();

r[abs(value)].line=wherey();

r[abs(value)].ch=check(Hhead,r[abs(value)].line,r[abs(value)].col);

value++;

}

colorview(Hhead,wherex(),wherey());

}

/*用不同的前背景色显示选择的字符*/

void colorview(Hnode *Hhead,int x,int y)

{

int i;

view(Hhead);/*重新显示所有文本字符*/

for(i=0;i

{

gotoxy(r[i].col,r[i].line);

textbackground(7);

textcolor(0);

if(r[i].ch!=13&&r[i].ch!=-1)

cprintf("%c",r[i].ch);

if(r[i].ch==13||r[i].ch==-1)

cprintf(" ");

}

gotoxy(x,y);

}

7.菜单控制

菜单控制的工作由menuctrl(Hnode*Hhead,int A)函数和drawmenu(int m,int n)函数配合完成的。

(1) 通过menuctrl(Hnode*Hhead,int A)函数,完成调用菜单光带调和选取菜单选项的任务。

(2) 通过drawmenu(int m,int n)函数,完成第m%3项菜单的绘制,并将光带条至于第m%3项的第n%b个菜单选项上,为相应菜单所拥有的菜单选项个数。

void drawmenu(int m,int n) /*画菜单,m:第几项菜单,n:第m项的第n个子菜单*/

{

int i;

if(m%3==0) /*画File菜单项*/

{

window(8,2,19,9);

textcolor(0);

textbackground(7);

for(i=0;i<7;i++) /*在上面定义的文本窗口中先输出7个空行*/

{

gotoxy(1,1+i);

insline();

}

window(1,1,80,25);

gotoxy(7,1);

for(i=1;i<=7;i++)

{

gotoxy(8,1+i);

cprintf("%c",179); /*窗口内文本的输出函数,在窗口左边输出 | */

gotoxy(19,1+i);

cprintf("%c",179); /*窗口内文本的输出函数,在窗口右边输出 | */

}

for(i=1;i<=11;i++)

{

gotoxy(8+i,2);

cprintf("%c",196); /*窗口内文本的输出函数,在窗口上边输出 - */

gotoxy(8+i,9);

cprintf("%c",196); /*窗口内文本的输出函数,在窗口下边输出 - */

}

textbackground(0);

gotoxy(10,10); cprintf(" "); /*输出下边的阴影效果*/

for(i=0;i<9;i++)

{

gotoxy(20,2+i);

cprintf(" "); /*输出右边的阴影效果*/

}

/*以上为显示菜单项的外观*/

textbackground(7);

gotoxy(8,2); cprintf("%c",218); /*输出四个边角表格符*/

gotoxy(8,9); cprintf("%c",192);

gotoxy(19,2); cprintf("%c",191);

gotoxy(19,9); cprintf("%c",217);

gotoxy(9,3); cprintf(" New ");

gotoxy(9,4); cprintf(" Open ");

gotoxy(9,5); cprintf(" Save ");

gotoxy(9,6); cprintf(" Save as");

for(i=1;i<=10;i++)

{

gotoxy(8+i,7);

cprintf("%c",196); /*在Save as下输出一行分隔符*/

}

gotoxy(9,8); cprintf(" Exit");

textcolor(15); textbackground(0);

gotoxy(7,1);

cprintf("%c %c File %c %c",179,17,,179);

switch(n%5)

{

case 0:gotoxy(9,3); cprintf(" New "); break;

case 1:gotoxy(9,4); cprintf(" Open "); break;

case 2:gotoxy(9,5); cprintf(" Save "); break;

case 3:gotoxy(9,6); cprintf(" Save as "); break;

case 4:gotoxy(9,8); cprintf(" Exit "); break;

}

}

/********************************************************/

if(m%3==1) /*画Edit菜单项*/

{

window(28,2,38,7);

textcolor(0);

textbackground(7);

for(i=0;i<5;i++)

{

gotoxy(1,1+i);

insline();

}

window(1,1,80,25);

gotoxy(27,1);

for(i=1;i<=5;i++)

{

gotoxy(28,1+i);

cprintf("%c",179);

gotoxy(39,1+i);

cprintf("%c",179);

}

for(i=1;i<=11;i++)

{

gotoxy(28+i,2);

cprintf("%c",196);

gotoxy(28+i,7);

cprintf("%c",196);

}

textbackground(0);

gotoxy(30,8); cprintf("

for(i=0;i<7;i++)

{

gotoxy(40,2+i);

cprintf(" ");

}

textbackground(7);

gotoxy(28,2); cprintf("%c",218);

gotoxy(28,7); cprintf("%c",192);

gotoxy(39,2); cprintf("%c",191);

gotoxy(39,7); cprintf("%c",217);

gotoxy(29,3); cprintf(" Cut

gotoxy(29,4); cprintf(" Copy

");

");

");

gotoxy(29,5); cprintf(" Paste ");

gotoxy(29,6); cprintf(" Clear ");

textcolor(15); textbackground(0);

gotoxy(27,1);

cprintf("%c %c Edit %c %c",179,17,,179);

switch(n%4)

{

case 0:gotoxy(29,3); cprintf(" Cut "); break;

case 1:gotoxy(29,4); cprintf(" Copy "); break;

case 2:gotoxy(29,5); cprintf(" Paste "); break;

case 3:gotoxy(29,6); cprintf(" Clear "); break;

}

}

/*********************************************************/

if(m%3==2) /*画Help菜单项3*/

{

window(48,2,48,6);

textcolor(0);

textbackground(7);

for(i=0;i<3;i++)

{

gotoxy(1,1+i);

insline();

}

window(1,1,80,25);

gotoxy(47,1);

for(i=1;i<=5;i++)

{

gotoxy(48,1+i);

cprintf("%c",179);

gotoxy(59,1+i);

cprintf("%c",179);

}

for(i=1;i<=11;i++)

{

gotoxy(48+i,2);

cprintf("%c",196);

gotoxy(48+i,6);

cprintf("%c",196);

}

textbackground(0);

gotoxy(50,7); cprintf(" ");

for(i=0;i<6;i++)

{

gotoxy(60,2+i);

cprintf(" ");

}

textbackground(7);

gotoxy(48,2); cprintf("%c",218);

gotoxy(48,6); cprintf("%c",192);

gotoxy(59,2); cprintf("%c",191);

gotoxy(59,6); cprintf("%c",217);

gotoxy(49,3); cprintf(" ");

gotoxy(49,5); cprintf(" ");

for(i=1;i<=10;i++)

{

gotoxy(48+i,4);

cprintf("%c",196);

}

textcolor(15); textbackground(0);

gotoxy(47,1);

cprintf("%c %c Help %c %c",179,17,,179);

switch(n%2)

{

case 0:gotoxy(49,3); cprintf(" "); break;

case 1:gotoxy(49,5); cprintf(" "); break;

}

}

}

int menuctrl(Hnode *Hhead,int A) /*菜单控制*/

{

int x,y,i,B,value,flag=100,a,b;

x=wherex(); y=wherey();

if(A==F1) { drawmenu(0,flag); value=300; } /*显示File及其子菜单,并将光带显示在第一个子菜单上*/

if(A==F2) { drawmenu(1,flag); value=301; } /*显示Edit及其子菜单,并将光带显示在第一个子菜单上*/

if(A==F3) { drawmenu(2,flag); value=302; } /*显示Help及其子菜单,并将光带显示在第一个子菜单上*/

if(A==F1||A==F2||A==F3)

{

while((B=bioskey(0))!=ESC) /*选择用户按键*/

{

if(flag==0) flag=100;

if(value==0) value=300; /*此value为局部变量*/

if(B==UP) drawmenu(value,--flag); /*循环上下移*/

if(B==DOWN) drawmenu(value,++flag); /*循环上下移*/

if(B==LEFT) /*菜单项之间循环选择(左移)*/

{

flag=100;

drawmain();

window(2,2,79,23);

textbackground(9);

for(i=0;i<24;i++)

insline();

window(3,3,78,23);

textcolor(10);

view(Hhead);

drawmenu(--value,flag);

}

if(B==RIGHT)/*菜单项之间循环选择(右移)*/

{

flag=100;

drawmain();

window(2,2,79,23);

textbackground(9);

for(i=0;i<24;i++)

insline();

window(3,3,78,23);

textcolor(10);

view(Hhead);

drawmenu(++value,flag);

}

if(B==ENTER) /*选中某主菜单项的子菜单项(选中某项)*/

{

if(value%3==0) b=5; /*File下有5个子菜单项*/

if(value%3==1) b=4; /*Edit下有4个子菜单项*/

if(value%3==2) b=2; /*Help下有2个子菜单项*/

a=(value%3)*10+flag%b;/*a表示选择子菜单的编号*/

drawmain();

window(2,2,79,23);

textbackground(9);

for(i=0;i<24;i++)

insline();

window(3,3,78,23);

textcolor(10);

view(Hhead);

gotoxy(x,y);

if(a==0) return 100; /*New*/

if(a==1) return 101; /*Open*/

if(a==2) return 102; /*Save*/

if(a==3) return 103; /*Save As*/

if(a==4) exit(0); /*Exit*/

if(a==10) return Cx; /*Cut*/

if(a==11) return Cc; /*Copy*/

if(a==12) return Cv; /*Paste*/

if(a==13) return DEL;/*Clear*/

if(a==20) return 120; /* */

if(a==21) return 121; /**/

}

gotoxy(x+2,y+2);

}

/*若按键非F1、F2、F3*/

drawmain();

window(2,2,79,23);

textbackground(9);

for(i=0;i<24;i++)

insline();

window(3,3,78,23);

textcolor(10);

view(Hhead);

gotoxy(x,y);

}

return A;

}

8.文件操作

文件操作的的工作由save(Hnode*head)函数、saveas(Hnode*head)函数和opens(Hnode*Hp)函数配合完成。

(1) 通过由save(Hnode*head)函数,将head所指的行单链表中所指的各个列单链表中的数据的值写入文件,文件路径和文件名由用户指定。

(2) 通过saveas(Hnode*head)函数,完成与函数save相似的功能,即将字符内容另存至某一文件。

(3) 通过opens(Hnode*Hp)函数,完成从人以文本文件中读取内容,保存至行单链表和列单链表形式的数据结构中的任务。

(4) 新建文件的工作在main()函数中完成。

/*将head所指的行单链表中所指的各个列单链表中的数据域的值写入文件,文件路径和文件名由用户指定*/

void save(Hnode *head)

{

FILE* fp;

Hnode *q;

node *p;

int count=0,x,y;

char filename[10]; /*保存文件名*/

q=head;

clrscr();/*清屏*/

printf("Enter infile name,for example [c:]:");/*输入文件名格式*/

scanf("%s",filename); /*输入文件名*/

fp=fopen(filename,"w");

if(fp==NULL) /*打开文件失败*/

{

printf("n=====>open file error!n");

getchar();

return ;

}

do{

p=q->next; /*指向node类型的数据*/

while(p!=NULL)

{ if((int)p->ch==13)

{

fputc('n',fp);p=p->next; count++;

}

else

{fputc(p->ch, fp);

p=p->next;

count++;}

}

q=q->nextl;

}while(q!=NULL);

fclose(fp); /*关闭此文件*/

return ;

}

/*文件另存为:将head所指的行单链表中所指的各个列单链表中的数据域的值写入文件,文件路径和文件名由用户指定*/

void saveas(Hnode *head)

{

FILE* fp;

Hnode *q;

node *p;

int count=0,x,y;

char filename[10]; /*保存文件名*/

q=head;

clrscr();/*清屏*/

printf("Enter infile name,for example [c:]:");/*输入文件名格式*/

scanf("%s",filename); /*输入文件名*/

fp=fopen(filename,"w");

if(fp==NULL) /*打开文件失败*/

{

printf("n=====>open file error!n");

getchar();

return ;

}

do{

p=q->next; /*指向node类型的数据*/

while(p!=NULL)

{ if((int)p->ch==13)

{

fputc('n',fp);p=p->next; count++;

}

else

{fputc(p->ch, fp);

p=p->next;

count++;}

}

q=q->nextl;

}while(q!=NULL);

fclose(fp); /*关闭此文件*/

return ;

}

/*从任意文本文件中读取文件内容,保存至行单链表和列单链表形式的数据结构中*/

void opens(Hnode *Hp)

{

FILE* fp;

Hnode *q11,*q22;

node *p11,*p22,*hp;

char temp;

int count=0,flags=1;

char filename[10]; /*保存文件名*/

clrscr();/*清屏*/

printf("Enter infile name,for example [c:]:");/*输入文件名格式*/

scanf("%s",filename); /*输入文件名*/

fp=fopen(filename,"r");/*以只读方式打开文件,filename必须要存在*/

if(fp==NULL)/*打开文件失败*/

{ textbackground(2);

textcolor(13);

cprintf("open file error!");

getchar();

exit(0) ;

}

q11=Hp;

while(!feof(fp))

{ count=0;flags=1;

q22=(Hnode *)malloc(sizeof(Hnode));/*新建一个行单链表中的节点*/

p11=(node *)malloc(sizeof(node)); /*新建一个列单链表中的节点*/

while((temp=fgetc(fp))!=10&&count<=76&&!feof(fp)) /*循环结束,表示在单链表中一行处理完毕,开始新行*/

{ p22=(node *)malloc(sizeof(node));/*新建一个列单链表中的节点*/

if(flags==1) {hp=p22;flags=0;} /*hp保存列单链表中的首节点的地址*/

p22->ch=temp; p22->next=NULL;

p11->next=p22; p11=p22;

count++;

}

if(temp==10){ /*若为换行符,将其转换为回车符,因为在程序中,是按回车符处理的*/

p22=(node *)malloc(sizeof(node));p22->ch=13; p22->next=NULL;

p11->next=p22; p11=p22;

}

if(!feof(fp))/*若没此条件,文件最后一行会处理两次.*/

{q22->next=hp;q22->nextl=NULL; /*将存储了字符的新列单链表与行单链表中的新节点建立关联*/

q11->nextl=q22;q11=q22;}

}

fclose(fp);

Hp=Hp->nextl;/*因为Hp的所在节点的数据域为空,所以Hp=Hp->nextl*/

return ;

}

9.主函数

主函数控制着整个程序的流程,其具体操作流程可参见图.2.

void main()

{

char a;

int i,A,x,y,flag=0,b;

Hnode *Hhead,*q;

node *p1,*p2;

Hhead=(Hnode *)malloc(sizeof(Hnode)); /*为行单链表中首节点分配内存空间*/

q=Hhead; Hhead->nextl=NULL;

p1=p2=q->next=(node *)malloc(sizeof(node)); /*为列单链表中首节点分配内存空间*/

p1->ch=13; p1->next=NULL;

drawmain(); /*显示主窗口*/

window(2,2,79,23);

textbackground(9);

for(i=0;i<24;i++)

insline();

window(3,3,78,23);

textcolor(10);

while(1)

{

while(bioskey(1)==0) continue; /*等待用户按键*/

a=A=bioskey(0); /*返回输入的字符的键值*/

if(a>=32&&a<127) /*若输入为常规字符或回车键*/

{

if(check(Hhead,wherey(),wherex())<=0)/*当前位置没有字符且输入是常规字符,则执行添加字符操作*/

{

NUM++;

p2->ch=a;

putch(a);

if(NUM==76) /*连续输入满行,分别生成一个新的行单链表和列单链表节点*/

{

p2->next=NULL;

q->nextl=(Hnode *)malloc(sizeof(Hnode));

q=q->nextl; q->nextl=NULL; q->next=NULL;

p1=p2=q->next=(node *)malloc(sizeof(node));

p1->ch=13; p1->next=NULL;

NUM=0;

}

else /*连续输入未满一行,生成一个新的列单链表节点*/

{

p2->next=(node *)malloc(sizeof(node));

p2=p2->next;

p2->ch=13;

p2->next=NULL;

}

}

else /*当前位置有字符且输入是常规字符,则执行插入字符操作*/

{

x=wherex(); y=wherey();

insert(Hhead,wherey(),wherex(),a);

NUM++;

view(Hhead);

gotoxy(x,y);

}

}

/*若输入为回车键*/

if(a==13)

{

gotoxy(1,wherey()+1);

q->nextl=(Hnode *)malloc(sizeof(Hnode));

q=q->nextl; q->nextl=NULL; q->next=NULL;

p1=p2=q->next=(node *)malloc(sizeof(node));

p1->ch=13; p1->next=NULL;

NUM=0;

}

x=wherex(); y=wherey();

/*文本窗口中左移,当前光标不在窗口的第1列*/

if((A==LEFT)&&(x!=1)) gotoxy(wherex()-1,wherey());

/*文本窗口中左移,当前光标在窗口的第1列*/

if((A==LEFT)&&(x==1))

gotoxy(abs(judge(Hhead,wherey()-1)),wherey()-1);

/*文本窗口中右移,若当前光标的右边一位有字符*/

if((A==RIGHT)&&check(Hhead,wherey(),wherex())>0)

gotoxy(wherex()+1,wherey());

/*文本窗口中右移至下行的第1列,若当前光标位置没有字符且下行的第1列有字符*/

if((A==RIGHT)&&check(Hhead,wherey()+1,1)!=0&&check(Hhead,y,x)<=0)

gotoxy(1,wherey()+1);

/*右移*/

if((A==RIGHT)&&x==76) gotoxy(1,wherey()+1);

/*上移*/

if((A==UP)&&check(Hhead,wherey()-1,wherex())!=0)

gotoxy(wherex(),wherey()-1);

/*上移*/

if((A==UP)&&check(Hhead,wherey()-1,wherex())<=0)

{

if(judge(Hhead,wherey()-1)==0)

gotoxy(-judge(Hhead,wherey()-1)+1,wherey()-1);

else

gotoxy(-judge(Hhead,wherey()-1),wherey()-1);

}

/*下移*/

if((A==DOWN)&&check(Hhead,wherey()+1,wherex())!=0)

gotoxy(wherex(),wherey()+1);

/*处理BackSpace键*/

if(A==BACK) /*处理BackSpace键*/

{

flag=del(Hhead,wherey(),wherex()-1);

x=wherex()-1; y=wherey();

view(Hhead);

if(flag==0)

{

if(x!=0) gotoxy(x,y);

else gotoxy(x+1,y);

}

if(flag==1)

{

gotoxy(x+1,y);

flag=0;

}

}

/*处理菜单按键F1 F2 F3*/

if((A==F1)||(A==F2)||(A==F3)||(a<32||a>127))

{ A=menuctrl(Hhead,A);

if(A==100){main();} /*新建文件*/

if(A==101){ /*打开文件*/

Hhead=(Hnode *)malloc(sizeof(Hnode));

opens(Hhead);

getchar();clrscr();gotoxy(3,3);view(Hhead);

}

/*保存文件*/

if(A==102){save(Hhead);clrscr();cprintf("save

successfully!");getch();gotoxy(3,3);view(Hhead);}

/*文件另存为*/

if(A==103){saveas(Hhead);clrscr();cprintf("save

successfully!");getch();gotoxy(3,3);view(Hhead);}

/*帮助*/

if(A==120){clrscr();cprintf(" F1:File F2:Edit F3:Help ");

getch();gotoxy(3,3);view(Hhead);}

if(A==121){clrscr();cprintf("Abort:Version

Tel:XXXXXXXXXX");getch();gotoxy(3,3);view(Hhead);}

}

/*处理DEL键,删除当前位置的单个字符*/

if(A==DEL)

{

x=wherex(); y=wherey();

del(Hhead,wherey(),wherex());

view(Hhead);

gotoxy(x,y);

}

/*处理已经选定文本字符后,按DEL键的情况*/

if(A==DEL&&value!=0)

{

if(value>0)

x=wherex(), y=wherey();

else

x=r[0].col, y=r[0].line;

for(i=0;i

{

if(value>0)

del(Hhead,r[i].line,r[i].col);

if(value<0)

del(Hhead,r[abs(value)-1-i].line,r[abs(value)-1-i].col);

}

value=0; /*此value为全局变量*/

view(Hhead);

as

2.0

gotoxy(x,y);

}

/*处理Ctrl+x按键*/

if(A==Cx&&value!=0)

{

if(value>0)

x=wherex(), y=wherey();

else

x=r[0].col, y=r[i].line;

for(i=0;i

{

if(value>0)

del(Hhead,r[i].line,r[i].col);

if(value<0)

del(Hhead,r[abs(value)-1-i].line,r[abs(value)-1-i].col);

}

backup=value; /*保存r数组的有值元素的最大下标值*/

value=0; /*此value为全局变量*/

view(Hhead);

gotoxy(x,y);

}

/*处理Ctrl+c按键*/

if(A==Cc&&value!=0)

{

x=wherex(); y=wherey();

backup=value; value=0; /*此value为全局变量*/

view(Hhead);

gotoxy(x,y);

}

/*处理Ctrl+v按键*/

if(A==Cv&&backup!=0)

{

x=wherex(); y=wherey();

if(backup<0) /*Ctrl+右移键选定的文本,贴切此当前位置*/

for(i=0;i

insert(Hhead,y,x+i,r[i].ch);/*逐个插入*/

if(backup>0) /*Ctrl+左移键选定的文本,贴切此当前位置*/

for(i=0;i

insert(Hhead,y,x+i,r[backup-1-i].ch);

view(Hhead);

gotoxy(x,y);

}

/*快速预览*/

if(A==F10)

{

qview(Hhead);

view(Hhead);

gotoxy(x,y);

}

}

}

/*处理Ctrl+左移键或右移键*/

if(A==CL||A==CR) control(A,Hhead);

/*显示当前行列号*/

x=wherex(); y=wherey();

window(1,1,80,25);

textcolor(0);

textbackground(7);

gotoxy(10,25); /*第25行,第10列,输出当前行号wherey()*/

cprintf("%-3d",y);

gotoxy(24,25); /*第25行,第24列*/

cprintf("%-3d",x);

window(3,3,78,23);

textcolor(10);

gotoxy(x,y);

textcolor(10);

textbackground(1);


发布者:admin,转转请注明出处:http://www.yc00.com/news/1709539399a1635211.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信