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

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

                        第二章   汉字的输入和显示

    文本文件是由文字和符号构成的。编辑软件运行时,要通过屏幕这个“窗口”,对文字进行输入、删改、移动等操作,因此要编制一个文字编辑软件首先遇到的就是文字的输入和显示问题。本章着重介绍西文字符和汉字的输入、显示和显示模块的编程方法。

                               2.1 ASCII 代码

    一般来说,可显示的“文字”分为两大类,一类是 ASCII 字符,也就是我们常说的半角字符;另一类是全角字符,包括国标 1─9 区的全角字母、数字、符号和国标 16─87 区的汉字,以及 87 区以后用户用造字软件自行制作的扩展汉字。半角字符是单字节表示的字符,而全角字符最少要两个字节。用于屏幕显示的十六点阵汉字通常是用两个字节来表示的。
    全部 ASCII 码共 256 个,从 0─255(0─0xFF)。在中西文文字编辑程序中,通常将代码 0─31 (0─0x1F) 用于操作控制键,分别对应于 Ctrl+A─Ctrl+Z 组合键和回车、Esc、Tab 等键;代码 32─126(0x20─0x78)为空格和标准 ASCII 字符,可供在屏幕上显示和写入文本文件;代码 127─160(0x79─0xA0) 常用于代表各种特殊功能写入文件,如常用 141(0x8D) 代表排版行末的软回车;代码 161─255 (0xA1─0xFF) 在汉字操作系统中用作汉字机内码。
   
                            2.2 键盘扫描码和汉字的输入

    在编辑操作时,西文字符和汉字都是通过键盘输入的,编辑软件对键盘输入的信息要进行判断和处理。在 main() 函数中,调用了 Turbo C 的 bioskey(int cmd) 函数,这个函数定义在头文件 bios.h 中,它通过调用 BIOS INT16H 中断,执行各种键盘操作,当参数 cmd=0 时,返回按键的16位扫描码,返回值在 AX 寄存器中。如果扫描码的低八位不为 0,则为 ASCII码,如果低八位为 0,则高八位为扩展键代码,用以代表一些特殊键和组合键。当cmd=1 时,判断是否击键,cmd=2 时判断转换键状态。
    用 bioskey(0) 取出扫描码后,要将扫描码的高八位和低八位分开,将高位和低位分开的方法很多。bioskey(0) 的返回值在 AX 寄存器中,因此可用 Turbo C 的伪变量 _AH 和 _AL,_AH中为高八位,_AL中为低八位,将它们的值赋给两个 char 类型的变量就行了:

    unsigned char h,l;
    bioskey(0);           /* 读出击键扫描码,返回值在 _AX 中 */
    h=_AH;                /* 高位码放入 h */
    l=_AL;                /* 低位码放入 l */

    也可以采取二进制位运算的办法:

    int a;
    unsigned char h,l;
    a=bioskey(0);         /* 读出扫描码,放入 a */
    h=a>>8;               /* 右移 8 位,获得高位码 */
    l=a&0x00FF;           /* 和 0x00FF 按位与,获得低位 */

    在 main() 函数中采用的办法是将 bioskey(0) 的返回值赋给一个联合,这个联合的定义为:

    union inkey {
      unsigned char ch[2];
      int ii;
    } cc;

    联合是其域在内存中可被覆盖的结构。联合中有两个成员,一个是字符形数组 ch[2],这个数组只有两个一字节元素,共占两个字节;另一个是两字节的整形变量 ii。由于在联合中,ch[2] 和 ii 共享两字节的存储区,因此把函数 bioskey(0) 的返回值赋于 cc.ii,则 cc.ch[0] 即为键值的低八位扫描码,cc.ch[1]中为键值的高八位扫描码。
    这时就可以根据 cc.ch[0] 和 cc.ch[1] 的值判断按的是什么键,从而作出相应的反应。例如  cc.ch[0]=65,按键为“A”;如 cc.ch[0]=0, cc.ch[1]=73, 则为 PgUp 键;又如cc.ch[0]=50,cc.ch[1]=3,是英文键盘上的“2”键;而当 cc.ch[0]=50,cc.ch[1]=80 时,为右边小键盘上的数字键“2”,在后面第九章讲述用数字小键盘输入全角制表符时就要用到这样的判断。
    半角字符的 ASCII 码等于按键扫描码的低位字节,因此当输入半角字符时,如上面举例的那样,cc.ch[0] 中存放的就是它的 ASCII 码。
    在汉字操作系统环境下输入汉字却不是这样,汉字操作系统设置有各种各样的输入方法,各种输入方法都设计了自己的汉字编码,即用一串字母和数字的组合来代替汉字。输入汉字实际上就是在键盘上输入这些汉字编码,输入的编码经过键盘处理程序处理转换成汉字的机内码。这时 bioskey(0) 接收到的不是按键自身的扫描码,而是汉字的机内码。如在五笔字型输入方式下输入一个“大”字,在键盘上就要输入“大”字的五笔字型编码 dddd,五笔字型的键盘处理程序就把它转换成“大”字的机内码 0xB4F3 了。由于汉字机内码有两个字节,所以一个汉字的处理应执行两次 bioskey(0)。
    字符型数组 ch[ ] 采用无符号类型 (unsigned 类型),是考虑到 char 型变量的值域为 -128 至 127,全角字符内码大于 180 均表现为负值,不便于判断,而 unsigned char 型变量的值域为 0 至 255,全角字符内码值和常规表示方式就一致了。
    要知道按键的码值可以通过查表的方式,也可以用下面的测试程序进行实际测试取得。该测试程序除了测试按键和组合键的扫描码外,还可用于测试汉字的机内码,当测试汉字时显示两个字节的内码。由于 C 语言是一种编译型语言,它不能直接运行源程序,因此必须把 KEY.C 编译、连接成可执行文件 KEY.EXE 方可使用。

/* KEY.C 测试键盘扫描码和汉字内码的实用程序 */
#include "stdio.h"
#include "bios.h"
main()
{
   int i,p;
   unsigned char h,l;
   for(;;) {
      printf("请按键或输入汉字。(按 ESC 退出)\n");
      for(i=0;i<2;i++) {
         p=bioskey(0);
         h=p>>8;
         l=p&0x00FF;
         if(l==27)  return;
         printf("      高位:%d (0x%x)  低位:%d (0x%x)\n",h,h,l,l);
         if(l<0xA1) break;
      }
   }
}

                           2.3 西文状态下字符的显示

    先看一看在西文状态下字符是怎样显示的。在西文软件中只要显示半角的 ASCII 码字符,要在屏幕指定位置显示一个 ASCII 字符,通常有以下两种方法:
    一种是调用中断的办法。先移光标至指定位置,再调用 ROM BIOS INT10H 中断第 9 号功能或调用 INT21H 中断第 2 号功能来实现。用 C 语言编写的光标定位和调用  ROM BIOS 中断显示一个字符的程序清单如下:

union REGS r;                   /* 定义一个联合 */
........
goto_xy(int x,int y)            /* 移光标至 x 行 y 列 */
{
   r.h.ah=2;                    /* 功能号 2 放入 AH 寄存器 */
   r.h.dl=y;                    /* 列坐标放入 DL 寄存器 */
   r.h.dh=x;                    /* 行坐标放入 DH 寄存器 */
   r.h.bh=0;                    /* 页号 0 放入 BH 寄存器 */
   int86(0x10,&r,&r);           /* 调用 INT10H 中断 */
}

write_char1(int x,int y,char ch,int attrib) /* 调用中断方式在指定位置显示字符 */
{
   goto_xy(x,y);                /* 移光标至 x 行 y 列 */
   r.h.ah=9;                    /* 功能号 9 放入 AH 寄存器 */
   r.h.bh=0;                    /* 页号放入 BH 寄存器 */
   r.x.cx=1;                    /* 显示次数放入 CX 寄存器 */
   r.h.al=ch;                   /* 显示字符放入 AL 寄存器 */
   r.h.bl=attrib;               /* 显示属性放入 BL 寄存器 */
   int86(0x10,&r,&r);           /* 调用 INT10H 中断 */
}

    另一种方法是在文本显示方式下,直接写字符显示缓冲区,也就是通过 CPU 访问存放 ASCII 字符点阵字模的 ROM 字符发生器,取出字模直接送入屏幕显示缓冲区的指定地址,再由显示卡自动从显示缓冲区中取出信息,显示在屏幕对应位置上。通常 256 个 ASCII 字符的点阵字模放在显示卡的 ROM 字符发生器中,通过 CPU 访问 ROM 字符发生器,并寻址定位到要显示字符点阵字模的首址,这个地址码实际上就是字符在字符发生器中存放的顺序码。对ASCII字符而言:

    地址码 = ASCII 码

    字符显示缓冲区是一块 RAM 芯片,这个内存区域用于存放屏幕上每个字符的内码和属性,不同的显示卡显示缓冲区的起始绝对地址是不一样的,单显卡上的这个起始绝对地址为0xB0000000,彩显卡为 0xB8000000。相对于起始地址的偏移量对应着屏幕上各个位置。偶数的偏移地址代表字符内码,奇数偏移地址代表字符属性。偏移量可用以下公式计算出来:

    偏移量 = (80×行号+列号)×2

    式中行号取 0━24,列号取 0━79。字符属性放在下一字节。在字符方式下,在彩显屏幕第 x 行、第 y 列以属性 attlib 写一字符的 C 程序如下:

char far *mem;
write_char2(int x,int y,char ch,int attlib) /* 字符方式下在指定位置显示字符 */
{
   char far *v;                     /* 定义一个 far 指针 */
   mem=(char far *)0xB8000000;      /* 显存首址 */
   v=mem+(x*80+y)*2;                /* 计算绝对地址 */
   *v++=ch;                         /* 写入字符,指针后移一字节 */
   *v=attrib;                       /* 填入字符属性 */
}
 
    上述函数中的 *v++=ch 里,由于增量运算符 ++ 在 v 后面,因此先赋值,再把指针移向下一字节。要注意,读写显示缓冲区必须采用 far 指针。只有这种 32 位的跨段指针,才能同时保存显示缓冲区的段地址和偏移地址,保证正确地寻址。这种写入方式与当前光标位置无关。字符属性用两个十六进制数字表达,第 0─3 位组成 16 种颜色表示字符的颜色,第 4─6 位组成 8 种颜色表示底色,第 7 位为 1 表示闪烁,为 0 表示不闪烁。如 0x17 代表蓝底白字。属性字节的格式如图 2.1。

                  7   6   5   4   3   2   1   0
               ┏━┳━┳━┳━┳━┳━┳━┳━┓
               ┃ B┃ R┃ G┃ B┃ I┃ R┃ G┃ B┃
               ┗━┻━┻━┻━┻━┻━┻━┻━┛
               │闪│          │字│          │
               │  │  背景色  │符│  字符色  │
               │  │          │加│          │
               │烁│          │亮│          │

                   图 2.1 字符属性字节的位结构

                             2.4 汉字的显示

    在中文状态下显示汉字则要复杂得多。全角汉字的机内码是由两个字节组成的,其值都在 161─255  (0xA1─0xFF) 之间。汉字的显示有字符和图形两种方式,在使用长城 014、CEGA 或 CVGA 等显示卡的微机上,以字符方式显示汉字,而在其他一些中文 DOS 系统下,多采用图形方式显示汉字。
    不论在字符方式还是在图形方式下显示汉字,都可用调用中断的办法。此时若要显示一个半角字符,只要把光标定位到指定位置,然后调用 BIOS INT10H 中断的第 9 号功能即可。但对于一个完整的汉字,由于它的机内码是由两个字节组成的,必须连续调用两次,所以即便只显示一个汉字也必须把它放在一个字符串变量中。具体地说,就是先定位光标,写入汉字的第一字节,再把光标后移一字节,写入汉字第二字节,汉字的显示属性由写入第一字节时指定的属性来确定。

write_string1(int x,int y,char *p,int attrib)     /* 显示字符串 */
{               /* x 屏幕行坐标,y 起始列坐标,p 显示字符串,attrib 属性 */
   int i;
   for(i=y;*p;i++)  write_char1(x,i,*p++,attrib); /* 在 i 列显示一字符 */
}

    在长城字符方式下显示汉字,也可用直接写显示缓冲区的办法,此类显示卡的字符显示缓冲区分为基本区和扩展区两个部分,基本区起始地址为 0xB8000000,扩展区的首地址在不同的显示卡上是不一样的: GW014 卡为 0xC0000000;CEGA 和 CVGA 卡为 0xB0000000。在长城系列微机上要判断显示卡的类型,可读出 3DA 端口的值。GW014 卡的 3DA 端口为一 8 位的状态寄存器,其高 4 位用于判别显示适配器。判断时读出 3DA 端口的值,如等于 1,可判定为 014 卡,否则为 CEGA 或 CVGA 卡。可以写一个判断程序,根据判断确定长城机显示缓冲区基本区和扩展区的首址,并将它们放在全局指针变量 mem 和 mmm 中,以备写显示汉字模块时使用。

char far *mem,far *mmm;                    /* 基本区和扩展区地址指针 */
ad()                                       /* 根据 3DA 端口值确定显存首址 */
{
   int pp;
   mem=(char far *)0xB8000000;             /* 基本区首址 */
   pp=inportb(0x3DA);                      /* 读出 3DA 端口值 */
   pp/=0x10;                               /* 取高 4 位 */
   if(pp==1)  mmm=(char far *)0xC0000000;  /* 014 卡扩展区首址 */
   else  mmm=(char far *)0xB0000000;       /* CEGA,CVGA 卡扩展区首址 */
}

    上面程序中 pp/=0x10 的写法,等价于 pp=pp/0x10,但前一种写法代码更短,运行速度更快。
    显示缓冲区的基本区和扩展区都分几个显示页,可用 BIOS 功能对任意页进行读写,也可直接将数据写入任意页,在默认的情况下,自动选择 0 页为活动页。在 0 页上,写入基本区和扩展区的地址相对于首地址的偏移量均为:

    偏移量=(80×行号+列号)×2

    使用 014 卡、CEGA 卡和 CVGA 卡的长城系列微机均有一个 64KB 的中文字符显示存储器 VRAM,但不同的显示卡结构不尽相同。用 014 卡的 GW0520CH 机屏幕显示分 4 页,页间间隔为 0x2000,每页显示 28 行(包括汉字输入提示区的 3 行),每行 80 列,在第 1、2、3 页上显示时,则要分别将上述偏移量增加 0x2000、0x4000、0x6000。 使用 CEGA 或 CVGA 卡的长城机则共分 8 页,其中 0 页─6 页作系统显示用,可显示 25 行,每行 80 列,最后一页 (7 页) 作为汉字输入提示区使用,页间间隔为 0x1000。
    一个半角字符在显示缓冲区的基本区和扩展区各占用两个字节,在基本区第一字节(偶地址) 处写入字符的 ASCII 码,下一字节 (奇地址) 写入字符属性。扩展区的两字节均填 ''''\0'''' 。一个全角字符在显示缓冲区的基本区和扩展区各占四个字节,要先将汉字的机内码变为显示码,在基本区的第一字节写入全角字符显示码的低八位,在第二字节填入全角字符的第一属性(字符色和背景色),扩展区第一字节写入字符显示码的高八位,第二字节处填入全角字符显示码的第二属性 (上、下、左、右划线),基本区和扩展区的后两字节填什么对汉字显示均无影响,一般可填 ''''\0'''' 或填 0x1F 代表是全角字符的后半部分。由于半角和全角字符显示上的区别,在编写显示模块时,就要先判断是半角还是全角字符,再区别处理。
    长城系列微机的汉字库在 ROM 字符发生器中,把汉字机内码变为显示码的过程如图2.2。


                      ┌→区码┐        ┌→高位→高位显示码
        机内码→区位码┤      ├→地址码┤
                      └→位码┘        └→低位→低位显示码


                     图 2.2 汉字机内码到显示码的转换过程

 

    将汉字机内码转换成地址码的公式如下:

    汉字区位码 = 第一字节机内码 × 0x100 + 第二字节机内码 - 0xA0A0
    1-9 区地址码 = 区码×94 + 位码 + 255
    16 区以上地址码 = (区码-6)×94 + 位码 + 255

    地址码占两字节 16 位二进制数的 13 位, 把计算出来的地址码放在 _DX 伪变量中,_DH 即为高位码,_DL 为低位码。低位码即可作为显示码的低位码,地址码高位还要通过和 0x40 按位或来加上全角字符标志和 ROM 字符标志(第 6 位置 1、第 5 位置 0),才变为显示码的高位(见图 2.3)。


             高      位                          低     位
     7   6   5   4   3   2   1   0      7   6   5  4   3   2   1   0
   ┏━┳━┳━┳━┳━┳━┳━┳━┓┏━┳━┳━┳━┳━┳━┳━┳━┓
   ┃×┃  ┃  ┃  ┃  ┃  ┃  ┃  ┃┃  ┃  ┃  ┃  ┃  ┃  ┃  ┃  ┃
   ┗━┻━┻━┻━┻━┻━┻━┻━┛┗━┻━┻━┻━┻━┻━┻━┻━┛
     ┬  ┬  ┬  ────────────┬─────────────
     ↓  │  │                          ↓
     不  │  │                      13 位地址码
     用  │  └─→ 0:半角 1:全角
         └──→ 0: ROM 字符 1: RAM 字符


                         图 2.3 汉字显示码的数据结构


    下面是一个在字符方式下显示字符串的 C 语言函数:

write_string2(int x,int y,char *p,int attrib) /* 字符方式下显示字符串 */
{               /* x 屏幕行坐标,y 起始列坐标,p 显示字符串,attrib 属性 */
   int i;
   unsigned int vh,vl;
   char far *v,far *u;
   u=mmm+(80*x+y)*2;             /* 在扩展区的绝对地址 */
   v=mem+(80*x+y)*2;             /* 在基本区的绝对地址 */
   while(*p)  {                  /* 字符为真 (非 0) 则循环 */
      vh=*p++;                   /* 取出一字节机内码 */
      if(vh<0xA1)  {             /* 如为半角字符 */
         *v++=vh;                /* 该字节写入基本区,基本区地址指针加 1 */
         *u++=0;                 /* 扩展区填 ''''\0'''',指针加 1 */
         *v++=attrib;            /* 基本区填入属性值,地址指针加 1 */
         *u++=0;                 /* 扩展区填 ''''\0'''',指针加 1 */
      }
      else  {                    /* 如为全角字符 */
         vl=*p++;                /* 再取出一字节机内码 */
         i=vh*0x100+vl-0xA0A0;   /* 计算区位码 */
         vh=i/0x100;             /* 求区码 */
         vl=i-vh*0x100;          /* 求位码 */
         if(vh<0x0F)  _DX=vh*94+vl+255;    /* 1-9 区全角字符的地址码 */
         else  _DX=(vh-6)*94+vl+255;       /* 16 以上区汉字的地址码 */
         *u++=_DH|0x40;          /* 高位显示码写入扩展区,扩展区地址指针加 1 */
         *v++=_DL;               /* 低位显示码写入基本区,基本区地址指针加 1 */
         *u++=0;                 /* 扩展区填 ''''\0'''',指针加 1 */
         *v++=attrib;            /* 字符属性写入基本区,基本区指针加 1 */
         *u++=0;                 /* 基本区和扩展区后两字节填 ''''\0'''' */
         *v++=0;
         *u++=0;
         *v++=0;
      }
   }
}

    在 while(*p) 循环里,p 指针的移动在循环体内增量运算符 ++ 进行。当指针移到串尾,字符串尾以 ''''\0'''' 定界,其 ASCII 码值为 0,由于 *p 为 0,循环条件不成立,而跳出循环,结束字符串显示。    用调用中断显示汉字方式编写的软件无论在字符方式还是在图形方式都能正常地显示汉字,但在字符方式下,它的运行速度慢于直接写显示缓冲区。比较好的办法是,先测定当前屏幕的显示模式,确定是否是长城彩显的字符方式,然后分别采用相应的写屏方法。测定的方法是检查 0040:0049 存储单元中的值,此值为 0─3 是彩显字符方式。使用GW014、CEGA、CVGA 卡的长城机显示方式 0─3 时,屏幕字符显示均为 80 列×28 行。可以设立一个标志变量 vid,在字符方式直接写显示缓冲区时置 vid 为 1,调用 BIOS 显示功能时 vid 为 0,然后根据 vid 的值选择不同的写屏程序。

mod()                       /* 测显示模式 */
{
   unsigned char vmode;
   vmode=peek(0x40,0x49);   /* 取 0040:0049 存储单元的值 */
   if(vmode>3)  vid=1;      /* 如显示方式不为 0─3,调用 BIOS 显示功能 */
   else {                   /* 否则用直接写屏方式 */
      vid=0;                /* 置显示标志值为字符方式 */
      ad();                 /* 根据 3DA 端口值确定显存首址 */
   }
}
   
    在本书附录的 BJ 源程序清单中,把 ad()合并到了 mod()中,效果是一样的。

                      第三章   全屏幕编辑的屏幕显示

    屏幕界面是用户界面最重要的组成部分,对于全屏幕编辑软件来说,尤其是这样。屏幕界面通过最直观的形式进行人机间的信息交换,一个高效、友好的屏幕界面对于充分发挥软件的性能,提高操作者的操作效果和工作效率无疑是十分重要的。屏幕布局、内容安排、色彩选择等都关系到屏幕界面设计的质量,因此在设计编制时应该认真对待。

                                3.1 屏幕布置

    一个全屏幕编辑软件运行时屏幕上除了显示正在编辑的文本内容外,还必须提供许多其它的信息,如当前编辑的状态 (插入、表格线状态)、编辑的文件名、当前光标位置 (行号、列号、序号)、当前操作和下一步操作的提示、误操作的警告等,使操作者在任何时候都能通过屏幕观察到每一操作的结果和得到怎样继续操作的指示。如何在屏幕上合理地布置这些信息,是编辑软件设计的一项重要内容。
    图 3.1 屏幕布置图将整个屏幕划分为几个部分。设定 HH 为屏幕显示最大行坐标,如长城系列微机,屏幕总共有 28 行,最下面三行为汉字系统输入提示区,实际用户可利用行数为 25 行(0 行─24 行),HH 值即定为 24。我们可以把第 0 行至第 21 行用作编辑窗口,第 22 行建一个标尺行,第 23 行显示正在编辑的文件名、文本的当前行号、列号、字序号、“插入”和“表线”状态的提示等。第 24 行显示操作过程中的有关提示、提问和进行人机对话。当然屏幕如何布局完全可由设计者根据需要作出各种不同的安排。


                                                                  示意符列
                                                                      ↓
          ┌──────────────────────────────┐
          │             文字编辑方法                                  <│
          │    利用计算机进行中西文文字编辑,必须借助于               .│
          │文字处理软件。常用的中西文文字处理软件有中文               .│
          │WS、WPS、XE、HW等。                                        <│
          │    只有掌握编辑软件的使用方法,才能                        │
          │                                                            │
          │                                                            │
          │                                                            │
 编辑窗口 │                                                            │
 (0─H3行)│                                                            │
          │                                                            │
          │                                                            │
          │                                                            │
          │                                                            │
          │                                                            │
          │                                                            │
          │                                                            │
          │                                                            │
          │                                                            │
          │                                                            │
          │                                                            │
表尺行(H2)│----|----|----|----|----|----|----|----|----|----|----|---- │
信息行(H1)│插入    序:   161  行:   5 列:  21 文件名: AAA.TXT          │
提问区(HH)│请输入要找的字符串: 编辑软件                    请稍候..... │提示区
          └──────────────────────────────┘

                                 图 3.1 编辑屏幕


                            3.2 屏幕的文本编辑窗口

    编辑缓冲区的编辑数组在整个文本文件中的位置是动态的,它相对于文本文件来说就象一个移动的窗口,而屏幕的编辑文本显示区又是编辑数组的一个窗口,它在编辑数组中的位置也是动态的,计算机操作人员就是通过屏幕这个窗口,来观察和编辑文本文件的。屏幕编辑显示区所能显示文本的行数和列数都是很有限的,它相对于编辑数组位置的移动,不但有纵向的,而且有横向的,通常称之为上下滚屏和左右移屏。除了文末行在当前屏幕中这一特定情况时,屏幕可以包含文末行后的空白区外,屏幕不允许超出编辑数组中已占用的文本行,当屏幕将要移出编辑数组时,必须移动编辑数组在文本文件中的位置,来保障屏幕不移出数组。
    左右移屏时,可以一次移一个字节,也可一次移多个字节,这里我们设定一次移半屏,为了确定屏幕位置,设 m 为左右移屏的屏号,把显示数组行第 0 列的屏称为第 0 屏,每向右移过半屏,m 加 1。自左向右依次为第 0 屏、第 1 屏、第 2 屏......。(见图 3.2)
    

    文本行号     数组行号    屏幕行坐标            内 存 数 组
    xx-ss_x          0                ┏━━━━━━━━━━━━━━━━┓
                                      ┃                                ┃
                                      ┃                                ┃
                                      ┃                                ┃
                                      ┃  第 0 屏 │  第 2 屏 │  第 4 屏 │
     xx-x         ss_x-x         0    ┣━━┯━━╈━━┯━━╈━━┯━┻┪
                (屏幕首行)            ┃    │    ┃    │    ┃    │    ┃
      xx           ss_x          x  ─╂┄┄│┄┄┃    │    ┃    │    ┃
                 (当前行)             ┃    │    ┃    │    ┃    │    ┃
                                 HH   ┣━━┿━━┻━━┿━━┻━━┿━┳┛
                            (屏幕底行)┃    │ 第 1 屏  │  第 3 屏 │  ┃
                                      ┃                                ┃
                  ss_max              ┡━━━━━━━━━━━━━━━━┩
               (实用最大行)           │                                │
                                      │                                │
                                      │                                │
                   QB-1               └────────────────┘
              (最大可容行号)


                       图 3.2 屏幕与编辑缓冲区数组的关系


    在编辑程序中,一个文本行要在文本文件、编辑数组和屏幕上定位,因此它有三个行号,一个是它在整个文本文件中的绝对行号,设为 xx;一个是该行在编辑数组中的数组行下标,设为 ss_x;另一个是屏幕显示的行坐标,设为 x;文本行中的一个字符有两个列号,yy 为该字符位置在文本行中的列号,同时它也是编辑数组列下标;y 为屏幕显示的列坐标。
    文本列号、屏幕列坐标和屏号之间有如下关系:

    m=yy/BP
    y=yy-m*BP

    式中,常量标识符 BP 为屏幕每次移屏的字节数,它是在预处理语句中用  #define 定义的。BJ 编辑程序中取 BP=ZS/2,ZS 为屏幕每行显示文本字节数。由于相邻两屏实际上有半屏是重迭的,所以一个显示在屏幕右半屏的字符也可以显示在后一屏的左半屏,因此计算出的 m 如果不等于 0,常常还要减去 1。在上述两种可能的情况下,屏幕显示的列坐标值 y 也是不同的。通常显示器屏幕每行的显示列数为 80 列,在本书的实例中 ZS 取 76,屏幕最右一列用于示意符:“<”表示由硬回车结束的行,“.”表示软回车结束的行,“+”表示本行在当前屏幕未显示完,右面下一屏还有字符,文末行和其后的行无示意符。程序中用函数 comput() 来计算屏幕显示的有关参数。

comput()                         /* 根据 yy 计算屏号 m,y 坐标等参数 */
{
  m=yy/BP;                       /* 计算屏号 */
  if(m)  m--;                    /* 如不为 0 屏,屏号减一 */
  y=yy-m*BP;                     /* 计算屏幕显示列坐标 y */
  if(xx-x<0) x=xx;               /* 如xx-x小于屏幕行坐标,屏幕行坐标取当前行号 */
}


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

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