C语言读取汉字字模

前不久,在网上看到一个生成点阵字的网站。觉得很有意思!
到底什么是点阵字,点阵字和字模之间有什么关系?

让我们先看一个点阵汉字和一个英文字母:



            **         
 **************************        
       **              
       **              
       **              
       **              
       **    **         
 **************************        
       **              
       **              
       **              
       **              
       **              
       **     **        
******************************       


          ........              
          ...#....              
          ..###...              
          .##.##..              
          ##...##.              
          ##...##.              
          #######.              
          ##...##.              
          ##...##.              
          ##...##.              
          ##...##.              
          ........              
          ........              
          ........              
          ........              
          ........            



这就是点阵字,也就是根据字符的字模用符号画出来的,当然你可以把*号#号改成其它的任何符号都可以。
是不是觉得很有意思了?

汉字内码:
我们都知道,英文只有少数的几十个字符,在计算机中用一个字节可以很容易的表示出来(也就是ASCII码);而汉字由于结构本身的原因,数量很大,常用的也有几千个。显然计算机中按照英文字符的方式对处理汉字是不可取的。
由是前人们就将ASCII表的高128位很少用到的数值以两个为一组来表示汉字,这就是汉字的内码。而剩下的低128位则留给英文字符使用,即英文的内码。

看一个C程序示例:

程序代码 程序代码

main ( )
{
     unsigned char *s , *e = "A" , *c = "王" ;
    clrscr ( ) ;
    printf ( "English char =" ) ;
    s =e ;
     while ( *s ! =0 ) /*C的字符串以0为结束符*/
     {
        printf ( "%3d," , *s ) ;
        s + + ;
     }
    printf ( "/nChinease char=" ) ;
    s =c ;
     while ( *s ! =0 )
     {
        printf ( "%3d," , *s ) ;
        s + + ;
     }
    getch ( ) ;
}




编译运行以后,输出的结果为:
English char = 65,
Chinease char= 205,245,

查ASCII码字符表,很容易得到A的ASCII码为65。
我们可以查ASCII码表,得到出ASCII码对应的字符,那我们有什么办法来知道一个汉字内码对应的汉字了?

让我们先来认识一下区位码:
1981年5月,我国国家标准总局颁布了《信息交换用汉字编码字符集》(GB2312-80),简称国家标准汉字编码,也叫国标码。国标码共收进标准字符7445个。其中一级汉字3755个,二级汉字3008个,共计6763个汉字。 由于汉字的字符多,一个字节(即8位二进制代码)不足以表示所有的常用汉字。汉字国标码的每个汉字或符号在计算机中都使用2个字节(16位二进制)代码来表示。

在GB2312-80代码表中,纵向分为0~93,共94行。将行号称为区号,列号称为位号,分别有94个区和94个位。区号和位号用十进制表示,不足两位前面补0。这样每个汉字或符号都可用4位十进制表示。这就是我们常说的区位码。每一区共有94个汉字,而位记录该汉字在该区中的具体位置。(记得我们以前读书的时候,报考计算机考试,填写姓名都要我们用区位码填,我们都拿着自己的姓名一个个去查,我们查的那个东东就是区位码。现在想想,真是心寒呀。还好,我后来就没有查了,弄了个excel的宏。把班上人的姓名全部放到一个excel中,然后一点鼠标,哈哈……  区位码全自动出来了)。

现在我们知道,可以从区位码得到汉字,也可以从汉字反查出区位码。那么我们如何从内码得到区位吗?
汉字内码与区位码之间有一个简单的数学关系:  
内码高字节 = 区码+A0H = 区码+160  
内码低字节 = 位码+A0H = 区码+160

这个转换关系,我也不清楚原因,有谁知道还望指点。网上的大师们说这样转换,咱们就这样转换吧。

我们刚刚输出“王”的内码为:205(高字节),245(低字节)。由上面的换算关系,可以得到“王”字的区位码为:
区码=205-160=45
位码=245-160=85

查一下区位码表,4585所表示的汉字正好是“王”。
也可以打开输入法,选择内码输入法,然后选择区位码,输入4585,就会输出“王”字。


汉字字模:
现在让我们来认识一下什么是字模,所谓字模就是是汉字(或者字符)的形态。字模中保存了汉字的点阵信息,记录组成一个字符的点在何处显示,在何处不显示。我们只要得到汉字的字模,我们就可以很容易的程序来控制,把这个字符画出来。
我们刚刚得到的仅仅是汉字的内码,并根据汉字内码得到区位码,由区位码查表得到汉字。那么我们如何来得到汉字的字模了?
用过UCDOS(或者CCDOS,估计现在只有少数人还知道UCDOS是什么东东)的人应该知道,通过UCDOS可以让DOS系统下正确的显示中文目录。不通过UCDOS之类的软件,在纯DOS下,我们看到的中文目录会是一堆的乱码,而英文目录能够正确显示,这是什么原因了。
这是因为,英文的字模信息是一般固化在ROM里。中文字模信息一般记录在一个专门的文件中,这个文件在UCDOS和CCDOS中都有,文件名是HZK16。也就是16x16点阵的汉字字模信息,所谓16x16,就是说这个汉字在横向有16个点,和纵向16个点的区域里显示。还有24x24,32x32等。
我们也可以在UCDOS下找到英文字模的信息文件,文件名是ASC16,这里记录了英文字符的字模信息。ASC16文件记录的英文字符是8x16点阵的。这些记录字符字模信息的文件通常也叫字库文件。

ASC16文件的大小刚好为 4K (4,096 个字节),每一英文字符横向有8个点,纵向有16个点。也就是说要描述一个英文字符的点阵信息,必须要16*8=128bit=16Byte。而英文字符是一个字节表示,所能表示的最字符数为2的8次方,也就是256个字符(ASCII中是从0到255)。256个字符*16(每个字符要16个字节) = 4096 字节。 刚好为ASC16文件的大小。因此,我们要读取英文字模的信息,我们就先得到这个字符的ASCII码。
以得到字符"A"的字模信息为例:
假如我们要得到的字母A的字模信息,我们得到"A"的ASCII值为65,我们就可以算出字符"A"的字模信息在ASC16文件中的偏移量=(65*16)+1=
1041字节(注意这个数字,我们将在后面用程序进行验证),我们只需要从ASC16文件中1041字节开始读取16个字节就可以得到"A"的字模信息了。

同样,我们来看一下汉字字模。
汉字是16*16的所以描述一个汉字字模信息的大小为:16*16=256bit=32Byte,汉字是按照区位码的顺序来排列的。
我们以得到”王“字的字模信息为例:
我们先得到”王“字的内码为:205,245,根据内码与区位码的转换关系得到”王“字的区位码为:45,85。
由前面区位码介绍中,我们知道,每一区有94个汉字,位号表示在该区的位置。因此“王”字中区位码中的位置为:
94*(区号-1) + (位号-1) = 94*((45-1)+(85-1)) = 4220。
而每一个汉字占32个字节,因此我们得到“王”字在字库文件(HZK16)中的偏移量为:4220*32=
135040字节(注意这个数字,我们在后面将用程序进行验证)。我们只需要从HZK16文件中135040字节开始读取32个字节就可以得到“王”字的字模信息了。

完整的原程序代码如下:

程序代码 程序代码



/**********************************
* C 程序得到汉字字模信息
* by DreamTime [梦想年华]
* fanwsp@126.com    
* www.FreeAge.cn
* 2007-11
***********************************/


#include "stdio.h"


/**********************************
* 得到英文字符的字模信息,存入数组
* 参数:
*   *c:要得到字模信息的字符指针
*   buffer[]:存储得到字模信息的数组
* 无返回值
***********************************/

void getAscCode(char *c,char buff[])
{
    unsigned long offset;
    FILE *ASC;

    /*打开字库文件asc16*/
    if((ASC=fopen("asc16","rb"))==NULL){
       printf("Can't open asc,Please add it?");
       getch();
       exit(0);
    }
    offset = *(c)*16+1;             /*通过ascii码算出偏移量*/
    fseek(ASC,offset,SEEK_SET);     /*将文件指针移动到偏移量的位置*/
    fread(buff, 16, 1, ASC);        /*从偏移量的位置读取32个字节*/
    printf("ASCII:%d,offset:%d /n/r",*c,offset);
}


/**********************************
* 得到汉字字符的字模信息,存入数组
* 参数:
*   *c:要得到字模信息的字符指针
*   buffer[]:存储字模信息的数组
* 无返回值
***********************************/

void getHzKCode(char *c,char buff[])
{
    unsigned char qh,wh;
    unsigned long offset;
    FILE *HZK;

    /*打开字库文件hzk16*/
    if((HZK=fopen("hzk16","rb"))==NULL){
       printf("Can't open haz16,Please add it?");
       getch();
       exit(0);
    }
    /*区码=内码(高字节)-160  位码=内码(低字节)-160*/
    qh     = *(c) -0xa0;            /*10进制的160等于16进制的A0*/
    wh     = *(c+1) -0xa0;          /*获得区码与位码*/
    offset = (94*(qh-1)+(wh-1))*32L;/*计算该汉字在字库中偏移量*/
    fseek(HZK,offset,SEEK_SET);     /*将文件指针移动到偏移量的位置*/
    fread(buff,32,1,HZK);           /*从偏移量的位置读取32个字节*/
    printf("qh:%d,wh:%d,offset:%ld/n/r",qh,wh,offset);
}


/**********************************
* 根据字模信息输出英文字符
* 参数:
*   *mat:字模指针
*   *c1 :字模中为1的点显示的字符,也就是前景字符
*   *c2 :字模中为0的点显示的字符,也就是背景字符
* 无返回值
***********************************/

void printAscChar(char *mat,char *c1,char *c2)
{
  int i,j;
  for(i=0;i<16;i++)                 /* 8x16的点阵,一共有16行*/
  {
    for(j=0;j<8;j++)                /*横向一个字节8位,依次判断每位是否为0*/
        if(mat[i]&(0x80>>j))        /*测试当前位是否为1*/
            printf("%s",c1);        /*为1的显示为字符c1*/
        else printf("%s",c2);       /*为0的显示为字符c2*/
    printf("/n");                   /*输完一行以后,进行换行*/
  }
}


/**********************************
* 根据字模信息输汉字字符
* 参数:
*   *mat:字模指针
*   *c1 :字模中为1的点显示的字符,也就是前景字符
*   *c2 :字模中为0的点显示的字符,也就是背景字符
* 无返回值
***********************************/

void printHzKChar(char *mat,char *c1,char *c2)
{
  int i, j, k;
  for(i=0;i<16;i++)                 /*16x16点阵汉字,一共有16行*/
  {
    for(j=0;j<2;j++)                /*横向有2个字节,循环判断每个字节的*/
      for(k=0;k<8;k++)              /*每个字节有8位,循环判断每位是否为1*/
        if(mat[i*2+j]&(0x80>>k))    /*测试当前位是否为1*/
          printf("%s",c1);          /*为1的显示为字符c1*/
         else printf("%s",c2);      /*为0的显示为字符c2*/
    printf("/n");                   /*输完一行以后,进行换行*/
   }
}



/**********************************
* 主函数
***********************************/

void main()
{

    unsigned char *AscC  = "A";
    unsigned char *AscC1 = "*";
    unsigned char *AscC2 = " ";

    unsigned char *HzkC  = "王";
    /*汉字占两个字节,前景字符和背景字符都要有两个英文字符或一个中文字符,否则字体将变形*/
    unsigned char *HzkC1 = "**";
    unsigned char *HzkC2 = "  ";

    char *asc;
    char *hzk;
    char buffer1[16]; /*存储英文字模信息*/
    char buffer2[32]; /*存储中文字模信息*/

    /*输出英文字符*/
    getAscCode(AscC,buffer1);
    asc = buffer1;
    printAscChar(asc,AscC1,AscC2);

    /*暂停一下*/
    getch();
    clrscr();

    /*输出中文字符*/
    getHzKCode(HzkC,buffer2);
    hzk = buffer2;
    printHzKChar(hzk,HzkC1,HzkC2);

    getch(); /* 暂停一下 */
}





运行结果如下:


这是运行以后输出的界面。我们可以看到输出了“A”的ASCII为65,偏移量为:1041,与我们前面算出来的结果完全吻合。
按任意键,程序将输出中文字符信息:



我们可以看到,输出了“王”的区位码为:45,85,偏移量为:135040,与我们前面算出来的结果一样的。

到此,我们读取字模的信息就已经完成了,知道了原理,你也可以用画图的方式来显示汉字。
本文只是自己对于字模的一些肤浅的认识,有什么错误的地方,还望各位同仁给予批评指正。

原文链接:

http://www.freeage.cn/article.asp?id=162

 

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值