基于HZK16的汉字显示技术

265 篇文章 0 订阅
5 篇文章 0 订阅

http://blog.csdn.net/yanghai0321/article/details/7644412

  国标汉字字符集(GB2312-80)在汉字操作系统中以汉字库的形式提供,并对汉字库的结构做了统一规定。汉字库的结构如图:

        HZK16的GB2312-80支持的汉字有6763个,符号682个。字库有94个区,其中一级汉字有3755个,按声序排列,二级汉字有3008个,按偏旁部首排列。每个区有94个位,每个位存一个汉字。这样每个汉字在汉字库中有确定的区号和位号。区号在前,位号在后,合成一个4位十进制数字,这就是区位码。区位码用两个字节存放(GB2312汉字是由两个字节编码的,范围为A1A1~FEFE。A1-A9为符号区,B0到F7为汉字区),第一个字节表示区号,第二个字节表示位号。只要知道了区位码,就可以知道该字在字库中的地址。

        16×16点阵库中,每个点阵模用32个字节来描述,其中的每个点使用一个二进制位。当需要显示时,把某个汉字的16×16点阵信息直接送到显示器上,值为1的点在屏幕上显示一个亮点,值为0的点则不亮,这样就可以显示出相应的汉字。

        在国标字库中,每个汉字还可以使用国标码。国标码与区位码之间的换算关系:

        国标码的区号=区位码的区号+32(或20H)

        国标码的位号=区位码的位号+32(或20H)

        或   区码=区号-0xa0    (因为汉字编码是从0xa0区开始的,所以文件最前面就是从0xa0区开始,要算出相对区码)

               位码=位号-0xa0

 

        此外还有汉字内码。汉字内码是汉字信息处理系统内部表示汉字的编码,也称机内码。西文字符的机内码多采用一个字节来表示的ASCII码,有的系统则采用EBCDIC码。一般只使用7位来表示128个字符,而把高位用作奇偶校验(或者不用)。我国的国标GB2312-80规定,一个汉字用两个字节表示,目前规定每个字节也只用七位,其高位未作定义。为了保证系统的中西文兼容,意味着系统的机内码中必须保持ASCII(IBM-PC采用该码作为西文字符的机内码)的使用,同时又要允许汉字机内码的使用,并且使两者之间没有冲突。如果用GB2312-80中的国标码作为机内码,则在系统中同时存在ASCII码和国标码时,将会产生二义性。例如,机内有两个字节的内容分别为30H和21H,它们既可以表示汉字“啊”的国标码,又可以表示字符“0”和“!”的ASCII码。所以,原原本本地采用国标码作为汉字机内码是不行的,必须要加以适当的变换。
        国标码规定,组成两字节代码的各字节的最高位均为0,但在机内码表示汉字时,应将每个字节的最高位置1,以表示该编码是汉字,这种编码称作为变形国标码。这样作既解决了西文机内码与汉字机内码的二义性,又保证汉字机内码与国标码之间有极简单的对应关系。

       国标码两字节的最高位分别加1后,便得到机内码。“计”字机内码如图:

  

这样国标码与机内码存在一种简单的对应关系:

    机内码区号=国标码区号+128(或80H)

    机内码位号=国标码位号+128(或80H)

       因为知道了某汉字的内码,即可确定出对应的区位码,知道了区位码,就可以找出该汉字字模在字库中的存放地址,从该地址中调出对应汉字的32个字节的字模信息,就可以显示出16×16点阵组成的该汉字了。

       从上文的分析可知,汉字内码与区位码存在固定的转换关系,设某汉字内码的十六进制数表示为0xkkjj,则相应区位码的区号qh和位号wh分别为:

       qh=0xkk-0xa0;

       wh=0xjj-oxa0;

      若用十进制数表示内码为c1c2,则

      qh=c1-160;

       wh-c2-160;

      即区位码qw=100*(c1-160)+(c2-160);相反要是知道了区位码qw,则也可以求得区号和位号:

       qh=qw/100;

        wh=qw-100*qh;

      因而该汉字在汉字库中离起点的偏移位置(以字节为单位),可有表达式offset=(94*(qh-1)+(wh-1))*32L(32L为长整形数)计算,其中假设int qh,wh,qw,long offset。

      对于16×16点阵汉字,其字模大小为(16×16)/8=32个字节,其输出时的排列方式如图:

 

        几种常用的汉字库中地址码offset的计算公式:

 1、ucdos中的字库CCLIB.DAT存放16×16点阵字模:

       offset=((qh-1)*94+(wh-1))*32L;

  2、CCDOS 2.13中的字库HZK16存放16×16点阵字模:

       offset=((qh-16)*94+wh-1+15*94)*32L;

  3、SPDOS 5.0的简单字库CCLIB.DAT存放16×16点阵字模:

       offset=((qh-7)*94+wh-1)*32L;

  4、CCDOS 2.13中字库hzk24存放24×24点阵字模:

        offset=((qh-16)*94+wh-1)*72;

          

         下面以一个小示例显示汉字:

  1. #include <stdio.h>   
  2.   
  3. int main(void)  
  4. {  
  5.     char chs[32];  
  6.     FILE *fp;  
  7.     int i,j;  
  8.     unsigned long offset;  
  9.     unsigned char hz[]="计";  
  10.     unsigned char qh,wh;  
  11.   
  12.     qh=hz[0]-0xa0;//获取区吗   
  13.     wh=hz[1]-0xa0;//获取位码   
  14.       
  15.     /* 
  16.     //int a=hz[0]; 
  17.     //int b=hz[1]; 
  18.     //printf("%x%x",a,b); 
  19.     //hz[0] hz[1]分别为汉字的机内码 
  20.     //offset=((hz[0]-0xa1)*94+(hz[1]-0xa1))*32;//注意0xa1十进制数为161 
  21.     */  
  22.     offset=((qh-1)*94+(wh-1))*32;//根据内码找出汉字在HZK16中的偏移位置  
  23.     if ((fp=fopen("HZK16","r"))==NULL)   
  24.         return 1;  
  25.     fseek(fp,offset,SEEK_SET);  
  26.     fread(chs,32,1,fp);  
  27.     for (i=0;i<32;i++)  
  28.     {  
  29.         if (i%2==0)   
  30.             printf("\n");   //每行两字节,16X16点阵  
  31.         for (j=7;j>=0;j--)  
  32.         {  
  33.             if (chs[i]&(0x01<<j))  
  34.                 /* 
  35.                   chs[i] & 0x80 判断第7位  
  36.                   chs[i] & 0x40 判断第6位  
  37.                   chs[i] & 0x20 判断第5位  
  38.                   chs[i] & 0x10 判断第4位  
  39.                   chs[i] & 0x08 判断第3位  
  40.                   chs[i] & 0x04 判断第2位  
  41.                   chs[i] & 0x02 判断第1位  
  42.                   chs[i] & 0x01 判断第0位 
  43.                   */  
  44.                 printf("计"); //由高到低,为1则输出'字',反之输出' ';  
  45.             else  
  46.                 printf("  ");  
  47.         }  
  48.     }  
  49.     fclose(fp);  
  50.     return 0;  
  51. }  
#include <stdio.h>

int main(void)
{
    char chs[32];
    FILE *fp;
    int i,j;
    unsigned long offset;
    unsigned char hz[]="计";
	unsigned char qh,wh;

	qh=hz[0]-0xa0;//获取区吗
	wh=hz[1]-0xa0;//获取位码
	
	/*
	//int a=hz[0];
	//int b=hz[1];
	//printf("%x%x",a,b);
	//hz[0] hz[1]分别为汉字的机内码
	//offset=((hz[0]-0xa1)*94+(hz[1]-0xa1))*32;//注意0xa1十进制数为161
	*/
    offset=((qh-1)*94+(wh-1))*32;//根据内码找出汉字在HZK16中的偏移位置
    if ((fp=fopen("HZK16","r"))==NULL) 
		return 1;
    fseek(fp,offset,SEEK_SET);
    fread(chs,32,1,fp);
    for (i=0;i<32;i++)
    {
        if (i%2==0) 
			printf("\n");   //每行两字节,16X16点阵
        for (j=7;j>=0;j--)
        {
            if (chs[i]&(0x01<<j))
				/*
                  chs[i] & 0x80 判断第7位 
                  chs[i] & 0x40 判断第6位 
                  chs[i] & 0x20 判断第5位 
                  chs[i] & 0x10 判断第4位 
                  chs[i] & 0x08 判断第3位 
                  chs[i] & 0x04 判断第2位 
                  chs[i] & 0x02 判断第1位 
                  chs[i] & 0x01 判断第0位
                  */
				printf("计"); //由高到低,为1则输出'字',反之输出' ';
            else
                printf("  ");
        }
    }
    fclose(fp);
    return 0;
}

得到的效果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值