C语言程序设计实践报告模板

中南大学

Central South University

C语言课程设计实践

题目名称: 实现一个简单的文本文件编辑系统

完成人姓名:郑艺颖

专业班级:电气信息类1117班

学号:0909111717

摘要

这个文本编辑器的功能类似windows的记事本,有新建、打开、保存、退出的文件操作功能,有复制、粘贴、剪切、清除的文件编辑功能,又有帮助与关于功能菜单。

在主函数main()中多次调用其他子函数是这个文本编辑器的最大特点。最主体的子函数莫过于drawmenu()画出文本编辑器的功能列表,其他还有各种功能的子函数,如drawmain()画出整个文本编辑器的主界面,如del()定义删除功能,在这里就不一一介绍,详见下面。

目录

一、            功能设计

……4

二、            详细设计

……7

三、运行结果

……34

四、结束语

……41

一、功能设计

通过对Windows自配的记事本的功能研究,并结合老师对本次课程设计的要求,让用户在我设计完成的文本编辑器中可以通过快捷和选择菜单项,完成基本的文本编辑和文件处理工作。该文本文件编辑系统应该至少具备有以下功能模块:文件操作模块、文本编辑模块、剪切操作模块、菜单控制模块和帮助及其他模块。

(一)文件操作模块

1、新建:新建功能应该要可以不只是单纯地把输入文本框清空,                                        而且还应该可以智能判断文本框中是否有数据,如果有数据未  保存或者保存过后又有数据变动,则会弹出提示信息询问是否  保存。新建功能可以通过选择File菜单上的New子菜单来实现。

2、打开:打开功能应该要可以通过记事本读入文件。打开功能可以通过选择File菜单上的Open子菜单来实现。在文件打开时,保存和另存为操作中,系统会提示用户输入文件路径及文件名。特别注意的是,当用户打开一个文件时,指定的文件必须存在,否则系统会报错。

3、保存:保存功能应该要可以直接将文本框中的内容记忆保存。保存功能可以通过选择File菜单上的Save子菜单来实现。

4、另存为:另存为功能应该要可以将文本框中的内容记忆保存,并且可以进一步把整个文件保存到指定地方。另存为功能可以通过选择File菜单上的Save as子菜单来实现。

5、退出:退出功能应该要可以直接退出文本文件编辑系统。退出功能可以通过选择File菜单上的Exit子菜单来实现。

(二)文本编辑器模块

1、添加:当光标所在位置及后面没有字符时,系统会以添加的方式输入字符。

2、插入:当光标所在位置及后面有字符时,系统会以插入的方式输入字符。

3、删除:用户可以通过Backspace键删除光标前一个字符,也可以使用Del键删除当前位置的字符。

4、选定:用户可以使用左移键(←)、右移键、上移键、下移键来移动光标位置。也可以通过Ctrl+左移键(或右移键)来选定多个字符。

(三)剪贴板操作模块

1、剪切:如果用户要剪切文本以便可以将它移动到其他位置,可通过Ctrl+X左移键(或右移键)先选定文本,然后选择Edit菜单上的Cut子菜单或按Ctrl+X快捷键来完成剪切任务。

2、复制:如果用户要复制文本以便可以将它到其他位置,必须先选定文本,然后选择Edit菜单上的Copy子菜单或按Ctrl+C快捷键来完成复制任务。

3、粘贴:如果用户要粘贴剪切或复制的文本,必须将光标置于要粘贴文本的位置,然后选择Edit菜单上的Paste子菜单或按Ctrl+V快捷键来完成粘贴任务。

(四)菜单控制模块

1、File子菜单:用户可以按F1功能键来完成该菜单项的调用,即显示该项菜单。用户可按光标上移或下移键在该菜单项的子菜单之间循环移动,也可以使用光标的左移键或右移键在3个菜单项之间循环移动。当光带移动到某个子菜单项上时,用户此时可使用Enter键来选取相关菜单选项。

2、Edit子菜单:用户可以按F2功能键来完成该菜单项的调用,即显示该项菜单。其他功能调用如上。

3、Help子菜单:用户可以按F3功能键来完成该菜单项的调用,即显示该项菜单。其他功能调用如上。

(五)帮助及其他模块

1、帮助:帮助功能可以提示使用者如何进入菜单中的各个项目。

2、关于:关于功能是描述此文本文件编辑系统的设计者。

3、快速预览:用户可按F10功能键来打开快速预览窗口,在快速预览窗口中没有功能菜单条。

二、详细设计

1、程序预处理。首先有头文件的加载,再有宏定义上下左右、退格、换行、复制、粘贴、剪切等热键,使用户可以直接从键盘上录入字符进行操控(字符即ASCII码字符),为了方便下面程序的运行,还要定义3个结构体,分别与剪贴板、列单链表和行单链表相关。

2main()主函数。因为程序是从main()函数开始执行,在main()函数结束的,所以在main()函数下。

(1)首先在main()中初始化一些全局变量及结构数组,接着调用drawmain()函数来显示主窗口,然后调用while(1)进入主循环,等待用户按键,最后程序根据用户的按键值,进行相应的处理,完成文本编辑的相关工作。

(2)若按键为常规字符,则继续判断在文本编辑区的当前光标位置有没有字符,若有字符,则调用insert()函数,将此字符插入在当前位置,否则直接输入即可。

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

(4)若按键为左,右,上,下,则执行gotoxy()操作,将光标移动至目标位置。

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

(6)若按键为Ctrl开头的按键,则执行与其相关的操作。具体来说,若为Ctrl+左移键(←),则将选定当前光标的位置开始向右的一个字符,若按住Ctrl键不放,连续按右移键,可以选定多个字符。若为Ctrl+左移键(←),则将执行与以上相同的操作。若为Ctrl+X键,则为剪切键(即将选定相关内容保存起来,且删除选定的字符)。若为Ctrl+C键,则为复制键(即将选定的相关内容保存起来)。若为Ctrl+V键,则调用insert()函数将保存起来的字符插入光标所在字符之前。

(7)若按键为F10键,则调用qview()函数,实现文本的快速预览。若按键为F1,F2,F3功能键,则调用menuctrl()菜单控制函数。若为F1键,则调用File菜单;若为F2键,则调用Edit菜单;若为F3键,则调用Help菜单。

具体程序如下:

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);

                     

                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 as successfully!");getch();gotoxy(3,3);view(Hhead);}

                 /*帮助*/

                 if(A==120){clrscr();cprintf("<Help> F1:File\nF2:Edit\nF3:Help ");

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

                 if(A==121){clrscr();cprintf("It is designed by Zheng Yiying ^.^ ");getch();gotoxy(3,3);view(Hhead);}

               }

              

               if(A==DEL)/*处理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<abs(value);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);

                      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<abs(value);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<abs(backup);i++)

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

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

                            for(i=0;i<backup;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);

        }

  }

3、接下来是要处理其他在主函数中被调用的子函数。

(1)void drawmain()

功能:画主窗口。

drawmain()函数用于在程序中绘画出主窗口,其中包括颜色、边框、叶脚等。

具体程序如下:

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

 {

          int i,j;

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

         textbackground(24);/*选择新的文本背景颜色*/

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

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

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

         {

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

             cprintf("%c",4);/*画出主窗口的左边界 */

             gotoxy(80,1+i);

             cprintf("%c",5);/*画出主窗口的右边界*/

         }

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

         {

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

              cprintf("%c",3);/*画出主窗口的上边界*/

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

              cprintf("%c",2);/*画出主窗口的下边界*/

         }

         gotoxy(1,1);   cprintf("%c",3);

         gotoxy(1,24);  cprintf("%c",3);

         gotoxy(80,1);  cprintf("%c",3);

         gotoxy(80,24); cprintf("%c",3);

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

         gotoxy(34,1); cprintf("%c  %c Edit %c  %c",186,17,16,186);/* | < > |*/

         gotoxy(60,1); cprintf("%c  %c Help %c  %c",186,17,16,186);/* | < > |*/

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

          textcolor(1);

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

         gotoxy(60,25);

         cprintf("By Zheng yiying ^.^ ");

 }

(2)void qview(Hnode*q)

功能:快速预览文本。

qview()函数让使用者可以通过F10键快速预览自己输入的文本。

具体程序如下:

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);

  }

3)viod view(Hnode*q)

功能:显示文本字符。

view()函数用于按行显示保存在单链表中的文本字符。

具体程序如下:

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)int check(Hnode *Hhead,int m,int n)

功能:检查字符。

check()函数用于在单链表中检查第m行,第n列位置的字符,若为常规字符,则返回该字符;否则返回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<m;i++)/*定位至行单链表中的第m个元素*/

            q=q->nextl;

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

        for(i=1;i<n;i++)/*定位至列单链表中的第n个元素*/

            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*/

  }

(5)int judje(Hnode*Hhead,int m))

功能:返回字数个数。

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

具体程序如下:

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

  {

        Hnode *q;

        node *p;

        int i,num=0;

        q=Hhead;

        for(i=1;i<m;i++)/*定位至行单链表中的第m个元素*/

            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,表示当前行中只有一个字符,且没有回车符*/

  }

6)int del(Hnode*Hhead,int m,int n)

功能:删除字符。

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

具体程序如下:

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<m;i++)/*定位至行单链表中的第m个元素*/

            q=q->nextl;

        p1=q->next;

        for(i=1;i<n-1;i++)/*定位至列单链表中的第n-1个元素*/

            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:表示有换位*/

  }

7)int test(Hnode*Hhead,int n)

功能:检验数据长度。

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

具体程序如下:

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<n;i++)/*定位至行单链表中的第n个元素*/

            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;

           }

      }

 }

8)viod insert(Hnode*Hheadint m,int n,char a)

功能:插入字符。

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

具体程序如下:

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<m;i++)/*定位至行单链表中的第m个元素*/

            q=q->nextl;

        p1=q->next;

        for(i=1;i<n-1;i++) /*定位至列单链表中的第n-1个元素*/

            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行开始的元素,使其满足规则*/

 }

9)void control(int A,Hnode*Hhead)

功能:控制左移、右移键。

control()函数用于对左移键(右移键)进行响应。

void control(int A, Hnode *Hhead)/*对控制键进行响应,A:按键的整数值,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)

          {    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());

  }

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

功能:用于用不同的前、背景色现实选择的字符。

void colorview(Hnode *Hhead,int x,int y)/*用不同的前背景色显示选择的字符*/

  {

           int i;

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

           for(i=0;i<abs(value);i++) /*value为数组下标*/

          {

                    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);

  }

11)void drawmenu(int m,int n)

功能:画菜单。

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

具体程序如下:

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,16,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(35,2,45,7);

            textcolor(0);

            textbackground(7);

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

           {

              gotoxy(1,1+i);

                insline();

            }

           window(1,1,80,25);

           gotoxy(34,1);

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

           {

              gotoxy(35,1+i);

                cprintf("%c",179);

              gotoxy(46,1+i);

                cprintf("%c",179);

            }

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

           {

                gotoxy(35+i,2);

                  cprintf("%c",196);

                gotoxy(35+i,7);

                  cprintf("%c",196);

            }

            textbackground(0);

           gotoxy(37,8); cprintf("            ");

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

           {

                gotoxy(47,2+i);

                  cprintf("   ");

           }

            textbackground(7);

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

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

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

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

           gotoxy(36,3);  cprintf("  Cut     ");

           gotoxy(36,4);  cprintf("  Copy    ");

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

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

            textcolor(15);  textbackground(0);

           gotoxy(34,1);

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

            switch(n%4)

            {

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

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

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

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

            }

      }

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

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

    {

           window(61,2,61,6);

            textcolor(0);

            textbackground(7);

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

            {

                    gotoxy(1,1+i);

                    insline();

            }

            window(1,1,80,25);

           gotoxy(60,1);

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

            {

                  gotoxy(61,1+i);

                    cprintf("%c",179);

                  gotoxy(72,1+i);

                    cprintf("%c",179);

            }

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

            {

                  gotoxy(61+i,2);

                    cprintf("%c",196);

                  gotoxy(61+i,6);

                    cprintf("%c",196);

            }

            textbackground(0);

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

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

           {

                gotoxy(73,2+i);

                  cprintf("   ");

           }

            textbackground(7);

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

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

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

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

           gotoxy(62,3);   cprintf("Help...   ");

           gotoxy(62,5);   cprintf("About...  ");

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

            {

                  gotoxy(61+i,4);

                    cprintf("%c",196);

            }

            textcolor(15);  textbackground(0);

           gotoxy(60,1);

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

            switch(n%2)

            {

                  case 0:gotoxy(62,3);  cprintf("Help...   "); break;

                  case 1:gotoxy(62,5);  cprintf("About...  "); break;

            }

    }

 }

12)int menuctrl(Hnode*Hhead,int A)

功能:菜单控制。

具体程序如下:

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;

                      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; /*Help... */

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

                      }

                       gotoxy(x+2,y+2);

              }

                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;

  }

13)void save(Hnode*head)

功能:存储文件。

save()函数用于将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:\\zhengyiying.txt]:");/*输入文件名格式*/

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 ;

}

14)void saveas(Hnode*head)

功能:将文件另存为。

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

具体程序如下:

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:\\zhengyiying.txt]:");/*输入文件名格式*/

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 ;

}

15)void opens(Hnode*Hp)

功能:打开文件。

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

具体程序如下:

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:\\zhengyiying.txt]:");/*输入文件名格式*/

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 ;

}

三、运行结果

(1)进入系统主界面。

(2)在文本编辑框可以输入字符。

这时,左下边会显示光标所在位置坐标。

(3)按F1进入文本操作模块。

通过上、下移键可以选择要操作的功能。

按F2进入文本编辑模块。

其他选择功能同文本操作模块。

按F3进入帮助及关于模块。

(4)按F10进入快速预览模式。

(5)通过F1子菜单中的Save 或Save as保存文本文件。

按照提示输入文件名。

通过F1子菜单中的Open打开指定路径的文本文件。

(6)通过F2子菜单中的Cut、Copy、Paste、Clear分别进行剪切、复制、粘贴、清除的操作。

首先应该先用Ctrl+左移(或右移)键选定待操作的字符,再选择需要进行的操作。

(7)按F3进入帮助及关于模块。

四、结束语

两周的课程设计很快就结束了,每次在电脑前一待就大半天,看程序看得我眼冒金星。但是看着自己的程序经过一次又一次的调试然后从初步成型到最后运行成功,心里倒是欢喜。

在这一段时间里,我明白了,要弄好一个课程设计并不容易,其中最重要的应该是要有良好的编程习惯。比如一些小细节,明明自己是知道的,但往往因为一不小心,到最后找错误时就要花好几倍的时间去寻找去改正。另外,编程时,一定要学会使用并且经常使用注释,不然可能第二天一觉醒来,就不知道自己编的一句话到底是有什么用处。

在课程设计过程中,让我意识到了自己在C语言学习上还有很多不足,因为在设计过程中,我还时常要向他人学习请教,或者还要不断去查找书籍或上网找资料,有时甚至照着书上照搬照抄。未来的学习之路还有很长,我不能再只是纸上谈兵地学习知识了,要动手实践,要把书上的知识用到实处,和实际联系,这样才能不断进步,在编程上有长足的进步!

相关推荐