当前位置:网站首页>>电脑技术>>C、C++语言>>我的著作 双击自动滚屏
中西文全屏幕编辑程序的设计和编制(一)

发表日期:2006年1月1日      已经有5772位读者读过此文

                  C 语言实践──中西文全屏幕编辑程序的设计和编制

                                      前   言

    文字编辑软件是在计算机上使用最广泛的软件之一,一个好的编辑软件可以大大地提高使用者的工作效率。编辑程序往往又是许多优秀软件的组成部分,如人们常用的 dBASEⅢ、Turbo C 等都带有自己的编辑器。怎样设计编制一个编辑软件,是不少软件工作者感兴趣的问题。本书通过讲述怎样用 C 语言编写一个全屏幕中西文编辑软件,使读者既能了解编写字处理软件的思路和方法,又能从中学到许多 C 语言编程的技巧。
    用 C 语言编程, 比用其它高级语言更能充分地发挥计算机硬件的功能,它又比汇编语言易学、可读性强。C 语言的这些突出优点,吸引了越来越多的人学习和应用 C 语言。随着学习的深入,许多人已经不满足于从一般的 C 语言教科书中了解一些 C 语言的基础知识和编制一些简单的小程序。因此,编写本书的又一目的就是通过对 C 语言编程技巧深入浅出的讲解,帮助读者实现从一个 C 语言初学者到高级程序员的过渡。
    本书共分十一章,第一章讲述全屏幕编辑软件的主体结构和设计构思;第二章分析汉字的输入及显示的特点和实现方法;第三章介绍全屏幕编辑软件的屏幕显示和主要显示模块的设计编制;第四章介绍文件目录列表、文件名输入和辅助文件名的建立;第五至十一章讲述各种编辑功能函数的编制。
    本书最后提供了一个用 Turbo C 编写的完整的全屏幕字处理软件 BJ 的源程序清单,我们在几乎每一条语句后面都加了中文注释,以帮助读者比较容易地了解各语句的含义。当你完全读懂了这个程序后,一定会感到受益匪浅的。如果把这个程序正确无误地输入微机中,并且用 Turbo C 2.0 编译、连接成可执行文件,得到的就是一个非常实用的全屏幕文字编辑软件。这个软件能实现文字的输入和删除,文本行的插入和删除,上下翻屏,左右移屏,字符串的查找和替代,文本排版,字块的设置、删除、移动、拷贝、写盘,外部文件的加入,文本的打印等功能,并可用键盘右侧的数字小键盘输入中文制表符。
    由于这不是一本讲述 C 语言基础知识的入门教材,所以我们没有从 C 语言最基本的概念说起。对于刚刚接触 C 语言的读者,建议能找一本 Turbo C 的入门书作为参考,对照着阅读本书,这样对迅速掌握 C 语言是很有好处的。如果你已是一个熟练的 C 语言的编程员,这本书也一定会给你许多新的启发。本书中的许多设计思想和子函数模块,在用 C 语言编制其他软件时也是非常有用的。
    本书既可以供广大计算机软件设计人员在设计软件时参考,又可作为高等院校计算机软件专业的教学参考书。由于水平所限,书中难免有错误和不当之处,恳请广大读者批评指正。

 

                                                        作 者

                                                    一九九三年九月
   


                              目      录
 

    前言
    第一章 设计全屏幕文字编辑软件的总体构思
       1.1 全屏幕编辑软件的基本框架
       1.2 主要全局变量简介
       1.3 编辑程序的主控模块
       1.4 在内存中开辟一个编辑缓冲区
       1.5 建立临时文件
       1.6 一个老文件的编辑过程
    第二章 汉字的输入和显示
       2.1 ASCII 代码
       2.2 键盘扫描码和汉字的输入
       2.3 西文状态下字符的显示
       2.4 汉字的显示
    第三章 全屏幕编辑的屏幕显示
       3.1 屏幕布置
       3.2 屏幕的文本编辑窗口
       3.3 左右移屏的实现
       3.4 文本行显示函数 disp() 的设计
    第四章 文件名的输入和目录显示
       4.1 字符串输入函数的建立
       4.2 当前目录中文件名的列表显示
       4.3 文件名的输入和辅助文件名的建立
    第五章 字处理的基本操作
       5.1 编辑功能函数编制的要点
       5.2 字符输入的基本操作
       5.3 光标的移动 
         5.3.1 光标的右移
         5.3.2 光标的左移
         5.3.3 光标的上移
         5.3.4 光标的下移
       5.4 上下翻屏
       5.5 回车键的输入
       5.6 退格键和 Del 键的使用
       5.7 行的插入、删除和局部删除
         5.7.1 插入一个空行
         5.7.2 删除一行
         5.7.3 删至行首
         5.7.4 删至行末
       5.8 迅速改变光标位置
         5.8.1 快速移至行首
         5.8.2 快速移至行末
         5.8.3 直接移至文首
         5.8.4 直接移至文末
         5.8.5 移至指定行
    第六章 字块操作和外部文件的插入
       6.1 字块的定义和取消
         6.1.1 字块的定义
         6.1.2 取消字块的定义
       6.2 当前光标位置移至块首
       6.3 字块的删除
       6.4 字块的拷贝
         6.4.1 把字块读入一个缓冲区
         6.4.2 把缓冲区中的块插入当前光标处
         6.4.3 字块拷贝的实现过程
         6.4.4 字块拷贝时文本行越界的处理
       6.5 字块的移动
       6.6 字块的存盘
       6.7 外部文件的插入
    第七章 字符串的搜索和替代
       7.1 字符串的搜索
       7.2 字符串的替代
    第八章 排版
       8.1 格式文件和非格式文件
       8.2 排版行宽的设置
       8.3 文本的排版操作
       8.4 字符输入时的排版
    第九章 用数字小键盘输入全角制表符
       9.1 用数字小键盘画表格线的构想
       9.2 表格线状态的切换
       9.3 用数字小键盘输入全角制表符的实现
    第十章 文本的存盘和退出编辑
       10.1 编辑文本的存盘
       10.2 存盘不退出编辑
       10.3 不存盘退出
    第十一章 文本的打印
       11.1 打印参数的输入
       11.2 打印输出操作
       11.3 打印机状况的测试
    附录:BJ 中西文全屏幕编辑软件源程序清单

 


                       第一章 设计全屏幕文字编辑软件的总体构思

    文字编辑软件是使用最为广泛的一种计算机软件,它经历了一个不断发展的过程,已经日趋完善。目前使用最为普遍的文字编辑软件有两类, 一类是行编辑软件, 如大家熟知的EDLIN 就属此类。行编辑程序的编辑以行为单位进行,对行的操作通过输入各种编辑命令来执行。对当前行中字符的编辑,操作者通常通过一些功能键配合字符输入来完成。另一类编辑软件是全屏幕编辑软件,操作者通过编辑命令、功能键或屏幕菜单选择执行各种编辑操作。全屏幕编辑软件比行编辑程序更方便灵活,功能更丰富。
    C 语言是一种结构式、模块式的程序设计语言,比其他高级语言更贴近计算机硬件。C语言是又一种编译性的语言,具有生成的目标代码紧凑、效率高,程序运行速度快的特点。它有着丰富的数据类型、运算功能,能直接进行位、字节、字和指针的操作。它带有的功能十分广泛的函数库中,包含了各种各样的输入/输出 (I/O)、字符串处理、文件管理、数据转换、内存管理等效率很高的函数,用 C 语言来编制中西文编辑软件无疑是非常合适的。
    本文通过对一个中西文全屏幕编辑软件 BJ 从总体设计到用 C 语言编制各功能模块的详细讲述,帮助读者了解全屏幕编辑软件的设计编制方法,并学习领会 C 语言的编程技巧。

                        1.1 全屏幕编辑软件的基本框架

    计算机软件是信息处理的工具,一个软件大体上应包括信息的输入、加工处理和输出三个方面,编辑软件也不例外。
    编辑软件工作的主要对象是文本文件,编辑操作可能是对一个已有的老文件的增删修改,也可能是建立一个新文件,因此输入文件名是编辑程序首先要进行的工作。
    文本文件是由许多个由字符组成的字符串行构成的,字符串行中每个字符的位置称为列,要对文本文件进行各种编辑操作,就必须在内存中开辟一个编辑缓冲区,如果编辑的是一个老文件,还必须把这个文本文件或其中的一部分读入这个缓冲区。
    全屏幕编辑是通过屏幕这个“窗口”观察文本和进行编辑操作的,还要利用屏幕提供有关信息和进行人机对话,所以编辑软件的设计,离不开对编辑屏幕显示的设计布局。程序进入编辑状态时,要先显示一个初始屏幕。
    在编辑过程中,要选择和实施各种功能的编辑操作,就必须建立各种编辑操作的功能模块,并提供选择它们的手段。在编辑工作终了时,要保存编辑工作的成果,并通过适当的途径退出运行。
    以上几项是构成了一个全屏幕编辑软件最基本的部分,可以用一个框图来描述全屏幕编辑软件的基本框架(见图 1.1)。

                             ┏━━━━━━━┓
                             ┃  建立文件名  ┃
                             ┗━━━┳━━━┛
                             ┏━━━┻━━━┓
                             ┃开辟编辑缓冲区┃
                             ┗━━━┳━━━┛
                             ┏━━━┻━━━┓
                             ┃  读入老文件  ┃
                             ┗━━━┳━━━┛
                             ┏━━━┻━━━┓
                             ┃ 显示初始屏幕 ┃
                             ┗━━━┳━━━┛
   ┏━━┳━━┳━━┳━━━━━━━┻━━━━━━━━┳━━┳━━┳━━┓
 ┏┻┓┏┻┓┏┻┓┏┻┓                            ┏┻┓┏┻┓┏┻┓  ┃ 
 ┃字┃┃删┃┃插┃┃搜┃                            ┃字┃┃文┃┃存┃  ┃ 
 ┃符┃┃除┃┃入┃┃索┃                            ┃块┃┃本┃┃  ┃  ┃ 
 ┃输┃┃文┃┃一┃┃字┃   . . . . . . . . . . . .  ┃的┃┃的┃┃  ┃  ┃ 
 ┃入┃┃本┃┃空┃┃符┃                            ┃存┃┃打┃┃盘┃  ┃ 
 ┃  ┃┃行┃┃行┃┃串┃                            ┃盘┃┃印┃┃  ┃  ┃ 
 ┗━┛┗━┛┗━┛┗━┛                            ┗━┛┗━┛┗┳┛  ┃
                                                                   ┃    ┃
                                                                 ┏┻━━┻┓
                                                                 ┃ 退  出 ┃
                                                                 ┗━━━━┛

                       图 1.1 全屏幕编辑软件的基本框架


    在实际工作中常常会出现这样的情况:一方面用户对软件功能越来越高的要求,使一些软件越来越复杂、庞大,另一方面用户又希望操作尽可能地简单,这又要求软件不能太复杂。怎样处理好这一对矛盾,根据实际需要找出最佳的方案是软件设计者首先要认真考虑的问题。总之,编辑软件设计时设置功能的多少应视用户的需要而定,在操作方法上要力求简单、高效。

                              1.2 主要全局变量简介

    本文示例的编辑程序 BJ 中使用的全局变量较多,这是根据该编辑程序的特点所确定的。这些全局变量定义在程序前部,它们的作用域是整个程序。使用较多的全局变量避免了调用函数时过多的参数传递,编程也相对容易些。又因为全局变量在程序中各函数里的表达形式相同,所以在某种程度上增强了程序的可读性。其缺点是削弱了一些与全局变量有关的函数的独立性,因此用 C 语言编程通常不主张用过多的全局变量。
    为了便于读者阅读本书中的程序,下面把 BJ 全屏幕编辑程序使用的主要全局变量作一简要介绍:

    ss[ ][ ] 编辑缓冲区数组 (简称编辑数组)。
    xx       文本行号,是当前行在文本中的绝对行号。
    yy       文本列号,既是当前列在文本中的列号,又是编辑数组的列下标。
    ss_x     数组行号,当前行在编辑数组中的行下标。
    ss_max   编辑数组实际存有文本行的最大行号。
    x        显示行号,当前行在屏幕上显示的屏幕行坐标。
    y        显示列号,当前列在屏幕上显示的屏幕列坐标。
    m        左右移屏屏号,首屏为 0。
    enq      排版行宽度。
    ksx      字块块首的文本行号,未定义块时置为 -1。
    ksy      字块块首的文本列号,未定义块时置为 -1。
    kwx      字块块尾的文本行号,未定义块时置为 -1。
    kwy      字块块尾的文本列号,未定义块时置为 -1。
    oa       临时文件 1 读写起始位置数组 wra[ ] 下标。
    ob       临时文件 2 读写起始位置数组 wrb[ ] 下标。
    wra[ ]   临时文件 1 读写地址数组。
    wrb[ ]   临时文件 2 读写地址数组。
    fp_rd    老文件读出的最后行在文本中的绝对行号。
    ttl_x    文末行行号,文本最后一行在文本中的绝对行号,先设一个足够大的数做初               值,待老文件全部读出后用已读出行数 fp_rd 代替。
    ser      当前光标处的字序数。
    old      文件标志,old=0 为新文件,old=1 为老文件。
    fp_end   老文件读完标志,fp_end=0 未读完,fp_end=1 已读完。
    ins      编辑状态标志,ins=0 非插入状态,ins=1 插入状态。
    vid      显示方式标志,vid=0 字符方式,vid=1 图形方式。
    tab      中文表格线标志,tab=0 非表格线状态,tab=1 表格线状态。
    blck     字块定义标志,blck=0 未定义块,blck=1 仅定义块首,blck=2 已定义块。
    chg      文件修改标志,chg=0 文件未修改,chg=1 文件已修改。
    *mem     长城字符方式显存基本区地址指针。
    *mmm     长城字符方式显存扩展区地址指针。
    *ddd     暂时存放字块内容的缓冲区。
    *hsz     提问行输入字符串的指针。
    *fnd     存放要搜索的字符串的指针。
    *mfile   存放正文文件名的字符串变量。
    *bfile   存放后备文件名的字符串变量。
    *file1   存放临时文件 1 (带扩展名 $1$) 文件名的字符串变量。
    *file2   存放临时文件 2 (带扩展名 $2$) 文件名的字符串变量。
    *fp      正文文件 mfile 的文件指针。
    *fp1     临时文件 1 的文件指针。
    *fp2     临时文件 2 的文件指针。
    *fp3     外部文件或字块写盘文件指针。

                            1.3 编辑程序的主控模块

    根据第 1.1 节提供的全屏幕编辑程序基本框架,可以编制编辑程序的主控函数 main()。main() 先建立文件名和临时文件名、后备文件名,为编辑文件开辟一个编辑缓冲区,如是老文件就读入一定数量的文本行至编辑数组,然后显示编辑屏幕。编辑操作的选择是通过判断击键的键值,确定是输入字符还是执行某种特定的操作,然后用 switch() 开关语句,分别作出相应的处理。除了要对文本进行增、删、修改等改变外,还要在屏幕上进行相应的显示。整个操作过程是循环进行的,每一种操作都建有相应的子模块。操作完成后,按 F1 存盘退出,并删除临时文件。因为各种中文操作系统较多地使用了 Ctrl 键或 Alt 键和 F1─F10 中一些功能键的组合,所以,BJ 程序里采用 Shift 键与 F1、F4 等功能键组成复合键,以尽可能地不和各类汉字操作系统发生冲突。

#define HH 24                        /* 屏幕显示总行数 */
#define H1 (HH-1)                    /* 信息行屏幕行号 */
#define QB 500                       /* 编辑数组最大行数 */
#define HC 255                       /* 编辑数组每行最大字节数控制值 */
union inkey {                        /* 定义一个存放击键值的联合 */
   unsigned char ch[2];
   int ii;
} cc;
........
main(int argc,char *argv[])          /* 主控函数 */
{
   int i;
   clss(0,HH);                       /* 清全部屏幕屏 */
   mod();                            /* 测显示模式 */
   for(i=0;i<QB;i++) {               /* 循环 QB 次 */
      ss[i]=malloc(HC);              /* 给数组行分配内存空间 */
      *ss[i]=0;                      /* 清为空串 */
   }
   mfile=malloc(16);                 /* 为正文文件名字符串变量分配内存空间 */
   *mfile=0;                         /* 字符串清为空串 */
   if(argc>1)  mfile=argv[1];        /* 如进入 BJ 时带形参 argv[1],赋给 mfile */
   filename();                       /* 输入文件名,建临时文件名,老文件读入 */
   hsz=malloc(HC);                   /* 为输入提问的字符串变量 hsz 分配空间 */
   fnd=malloc(HC);                   /* 为搜索字符串分配内存 */
   disp_t();                         /* 显示编辑屏幕 */
   Ins();                            /* 定初始状态为“插入”状态 */
   while(1) {                        /* 编辑操作的主循环 */
      xh();                          /* 显示当前行、列、序号 */
      coord();                       /* 显示标尺行 */
      goto_xy(x,y);                  /* 定屏幕光标位置 */
      cc.ii=bioskey(0);              /* 将按键扫描码读入一联合中 */
      if(cc.ch[0])  {                /* 如果击键扫描码低位不为 0 */
         switch(cc.ch[0]) {          /* 判断低位字节 */
            case 13:  Enter();       /* 输入回车键 */
                      break;         /* 跳出开关语句 */
            case 8:   Del();         /* 退格键删字 */
                      break;         /* 跳出开关语句 */
            case 14:  Ctrl_N();      /* Ctrl+N 插入行 */
                      break;         /* 跳出开关语句 */
            case 25:  Ctrl_Y();      /* Ctrl+Y 删除行 */
                      break;         /* 跳出开关语句 */
            case 20:  Ctrl_T(1);     /* Ctrl+T 删至行尾 */
                      break;         /* 跳出开关语句 */
            case 5:   Ctrl_E(1);     /* Ctrl+E 删至行首 */
                      break;         /* 跳出开关语句 */
            case 6:   Ctrl_F(1);     /* Ctrl+F 移至块首 */
                      break;         /* 跳出开关语句 */
            case 22:  Ctrl_V();      /* Ctrl+V 移动块 */
                      break;         /* 跳出开关语句 */
            case 11:  Ctrl_K();      /* Ctrl+K 块拷贝*/
                      break;         /* 跳出开关语句 */
            case 23:  Ctrl_W();      /* Ctrl+W 块存盘 */
                      break;         /* 跳出开关语句 */
            case 4:   Ctrl_D();      /* Ctrl+D 删除块 */
                      break;         /* 跳出开关语句 */
            case 16:  Ctrl_P();      /* Ctrl+P 打印文本文件 */
                      break;         /* 跳出开关语句 */
            case 18:  Ctrl_R();      /* Ctrl+R 外部文件插入 */
                      break;         /* 跳出开关语句 */
            case 27:  Esc();         /* ESC 键 */
                      break;         /* 跳出开关语句 */
            case 3:   bk();          /* Ctrl+C 退回DOS系统 */
            default:  Chr();         /* 如为字符键,输入字符 */
         }
      }
      else   {
         switch(cc.ch[1])  {         /* 如击键为特殊键 */
            case 81:  PgDn();        /* PgDn 键,向下翻屏 */
                      break;         /* 跳出开关语句 */
            case 73:  PgUp();        /* PgUp 键,向上翻屏 */
                      break;         /* 跳出开关语句 */
            case 59:  F1(1);         /* F1 键,存盘退出 */
                      break;         /* 跳出开关语句 */
            case 84:  Shift_F1();    /* Shif+F1 键,存盘不退出 */
                      break;         /* 跳出开关语句 */
            case 60:  F2();          /* F2 键,移到指定行 */
                      break;         /* 跳出开关语句 */
            case 61:  F3();          /* F3 键,输入格式文件排版行宽 */
                      break;         /* 跳出开关语句 */
            case 62:  F4();          /* F4 键,从光标处至本段末排版 */
                      break;         /* 跳出开关语句 */
            case 87:  Shift_F4();    /* Shift+F4 从当前光标处至文末连续排版 */
                      break;         /* 跳出开关语句 */
            case 63:  F5();          /* F5 搜索字符串 */
                      break;         /* 跳出开关语句 */
            case 88:  Shift_F5();    /* Shift+F5 继续搜索 */
                      break;         /* 跳出开关语句 */
            case 64:  F6();          /* F6 字符串替代 */
                      break;         /* 跳出开关语句 */    
            case 65:  F7();          /* F7 定义块首 */
                      break;         /* 跳出开关语句 */
            case 66:  F8();          /* F8 定义块尾 */
                      break;         /* 跳出开关语句 */
            case 90:                 /* Shift+F7 */
            case 91:  Shift_F7();    /* Shift+F8 取消块定义 */
                      break;         /* 跳出开关语句 */
            case 67:  F9();          /* F9 移到文首 */
                      break;         /* 跳出开关语句 */
            case 92:  Shift_F9();    /* Shift+F9 移到文末 */
                      break;         /* 跳出开关语句 */
            case 68:  F10();         /* F10 表格线功能开关转换 */
                      break;         /* 跳出开关语句 */
            case 77:  Right();       /* → 键,右移光标 */
                      break;         /* 跳出开关语句 */
            case 75:  Left();        /* ← 键,左移光标 */
                      break;         /* 跳出开关语句 */
            case 72:  Up();          /* ↑ 键,上移光标 */
                      break;         /* 跳出开关语句 */
            case 80:  Down();        /* ↓ 键,下移光标 */
                      break;         /* 跳出开关语句 */
            case 82:  Ins();         /* Ins 键,插入/非插入状态转换 */
                      break;         /* 跳出开关语句 */
            case 83:  Del();         /* Del 键,同退格键 */
                      break;         /* 跳出开关语句 */
            case 71:  Home();        /* Home 键 光标移到行首 */
                      break;         /* 跳出开关语句 */
            case 79:  End();         /* End 键 光标移到行末 */
                      break;         /* 跳出开关语句 */
         }
      }
   }
}

    QB、HC、HH、H1 等常量标识符在程序开头用宏定义命令 #define 定义,程序编译预处理时,凡在程序中出现 QB 的地方都用 500 代替。这样做便于修改,要修改这个常量,只要修改预处理语句一处就可以了。H1 用一个数学表达式 (HH-1) 定义,加括号是基于安全性的考虑。

                     1.4 在内存中开辟一个编辑缓冲区

    编辑缓冲区可以有多种形式,如一维的字符型指针、链表等,在本文中,采取二维数组型式。根据文本文件是由行和列组成的矩阵,我们很容易联想到,可以建立一个二维字符型数组作为文本文件的编辑缓冲区,把要编辑的文本或其一部分放在这个二维数组中。设这个数组为 ss[i][j],下标 i 对应于数组的行,下标 j 对应于数组的列。i 和 j 都应从 0 开始计数。如 ss[5][7] 即表示第 5 行的第 7 列,而 ss[0][0] 则表示第 0 行的第 0 列。
    例如,定义一个 500 行 (0-499),每行 255 个字节 (0-254) 的二维数组,由于整个数组占用内存很大,是跨段的,因此不能简单地用“char ss[500][255];”来定义,而要用一个指针数组 *ss[500] 来替代,并用库函数 malloc() 为其分配内存空间:

#define QB 500
#define HC 255
char *ss[QB];
............
int i;
for(i=0;i<QB;i++)  ss[i]=malloc(HC);

    这样,开辟的编辑缓冲区占用的内存空间即为:

    500×255=127500 (字节)

    编辑缓冲区数组的大小往往受到使用环境的限制,如果中文操作系统留给用户的空间较小,可以适当减小 QB 的值。这个指针数组的元素可用 *(ss[i]+j) 表达,也可等价地用数组下标方式 ss[i][j] 来表达,本文采用数组下标表示,是考虑到这样比用指针型式程序清晰性好些,更便于读者阅读。
    根据以上定义的编辑缓冲区数组,如果编辑的文本文件总行数不超出 500 行,每行又不超过 255 字节(包括软回车符),则完全可以在这个编辑缓冲区中进行操作。许多较简单的编辑器,在使用时限定被编辑文本文件的大小,就是以在编辑缓冲区能装下为前提的。


相关专题: 我的著作
专题信息:
  C语言速成(第三章 程序控制语句)(2006-1-1 13:51:28)[9517]
  C语言速成(第七章 联合、枚举和自定义数据类型)(2006-1-1 13:51:46)[10217]
  C语言速成(第六章 结构)(2006-1-1 13:52:05)[8907]
  C语言速成(第五章 指针)(2006-1-1 17:28:54)[4667]
  C语言速成(第四章 数组)(2006-1-1 13:52:42)[4812]

相关信息:
 没有相关信息
  打印本页
设为首页 | 加入收藏 | 联系我们 | 管理入口
皖ICP备05018956号
Copyright © 2003 J.W.SHEN All Rights Reserved
后台管理系统 V1.0 制作
皖公网安备 34018102340322号