免责声明:
        本文是作者在研究过程中的一篇文章,本着互联网共享、自由(free,应该不是“免费”)之精神发布于此。作者才疏学浅,孤陋寡闻,能力有限,对文中出现的术语及概念的描述多有不当之处,由于本文并非学术报告及论文,不对这些概念性东西进行深入调研,如需权威性解释,请自行查阅相关文献。文中错误的地方,欢迎在文后留言,趁作者还有激情研究之时,大家一直探讨,共同学习,天天向上。

计算机中存在许多种格式的编码,比如ASCII、GB2312、GBK、UTF-8,等等。汉字以“内码”的形式存储于计算机中。对于汉字的处理,有三种不同情况,一为汉字的输入,比如常用的拼音输入、五笔输入;二为汉字的存储,即以某一编码存储于计算机中,比如GB2312、utf-8;三为汉字的显示,比如网页(及其它如MS Word等软件)中显示的各种字体,如宋体、黑体。

如无特别说明,本文所说的字库,指“点阵字库”,大小为16*16,共占32字节空间,编码格式为GB2312。汉字库所用的文件为“HZK16”,该文件可以从网络下载。下方出现“汉字”的地方,可以理解为“中文字符”,即GB2312中的所有出现的字符。本文重点讲述汉字在终端中的显示(这是废话,题目都说了是“汉字显示”了),并附带讲述一下中英文混合显示,英文的显示不在此文讲述,如感兴趣,可以查看这篇文章: 点阵字体显示系列之一:ASCII码字库的显示 。 网上关于汉字字库显示的文章可用“泛滥”一词形容,因此,也不多这一篇文章了。

“点阵字库”,应该是与“矢量字库”相对应,搞过单片机的可能会对点阵屏比较熟悉。当年,为了在点阵屏上显示个汉字,搞了很久,从买点阵屏,到画板,到写程序,到调试,苦功下了不少(电子店里每种点阵屏封装不同,需要自己测试,还需要自己画电路板),结果还是不如人意,此后对点阵屏心有余悸——正如数据结构一样。不过在触摸屏上显示汉字,硬件方面的东西不用管,省了不少费事——这是后话,暂且按下不提(何况在编写此文时还未动手实践呢)。
        关于GB2312不详细描述了,网上资料很多。GB2312将所收录的字符分为94 个区,编号为01区至94区;每个区收录94 个字符(但不一定就是94个),编号01位至94 位。GB2312 的每一个字符都由与其唯一对应的区号和位号所确定。一个汉字(或GB2312中的字符)占用两个字节,由前一字节可得到“区号”(或者说前一字节为“区号”,取决于这两个字节是什么),由后一字节可得到“位号”(或者说后一字节为“位号”)。例如,GB2312中第一个汉字“啊”,编号为16 区01 位(1601),内码为0xB0A1。
        GB2312 字符集的区位分布表:
        区号    字数    字符类别
        01      94    一般符号
        02      72    顺序号码
        03      94    拉丁字母
        04      83    日文假名
        05      86    Katakana
        06      48    希腊字母
        07      66    俄文字母
        08      63    汉语拼音符号
        09      76    图形符号
        10-15            备用区
        16-55    3755    一级汉字,以拼音为序
        56-87    3008    二级汉字,以笔划为序
        88-94            备用区

完整的GB2312简体中文编码表可以在这个地址查看:http://www.latelee.org/yetanothertest/GB2312-80.html。里面出现的十六进制是内码,并非区位号。
        关于内码、国标码、区位码
        区位码是GB2312已经分好了的,区位码分别加上0x20就是对应的国标码,国标码再加上0x80就是对应内码。而内码就是在GB2312简体中文编码表看到的十六进制数据。区位号用十进制表示,而内码用十六进制表示,这些东西应试即可,写代码的了解一下就行了。

显示一个汉字就是搜索字库文件HZK16,找到这个汉字,而后读取这个位置的32个字节,再显示出来。
        1、打开字库文件
        字库文件为HZK16,当然使用fopen函数了。
        2、搜索汉字(寻址)
        了解了汉字区位码、内码的一些概念后,就可以知道如何找到这个汉字了。在我们输入汉字中,汉字已经在计算机中以某种编码形式存在了,以GB2312为例,如下面语句:
        unsigned char incode[] = "啊";

那么,incode将占用3个字节,前面说了,一个汉字占2个字节,最后一个字节是“\0”。如果分别打印incode[0]和incode[1]的话,将得到0xb0,0xa1这两个数。它便是“啊”的内码。当然,这是GB2312编码,如果是UTF-8,又不一样了。不信的话,可以使用百度和google分别搜索“啊”,查看浏览器地址栏出现的十六进制数(使用百度搜索“啊”,地址栏有“wd=%B0%A1”字样,其中的B0、A0便是GB2312编码中“啊”的内码)。
        题外话:经常在VC6.0下写代码的可能会经常碰到“烫烫烫烫烫烫烫烫”这个东西。有资料显示,VC6.0下,未手动初始化的内存(数组),编译器会自动初始化为0xcc的,而两个0xcc就是中文“烫”的内码。
        那么如何得到区号和位号?前面说了,区位号加上0x20是国标码,国标码加上0x80就是内码,则区位号等于内码减去0xa0。如下:
                        qh = incode[0]   - 0xa0;
                wh = incode[1] - 0xa0;

得到了区号和位号,还不行,还要知道如何在HZK16这个文件中找到“啊”这个汉字的偏移量。计算公式如下:
        offset = ( 94*(qh-1) + (wh-1) ) * 32;

得到的偏移量便是HZK16文件中“啊”的偏移量。经过计算,知道offset为0xb040,用hexdump看一下这个地址的32个字节数据:
        $ hexdump -C HZK/HZK16 | grep b040
        0000b040  00 04 2f 7e f9 04 a9 04  aa 14 aa 7c ac 54 aa 54  |../~.......|.T.T|

这种数据不直观,将这些数据以二进制形式写出来,16个二进制一行,将得到如下数据:
        00000000000000100 // 0x00 0x04
        00101111001111110
        11111001000000100
        10101001000000100
        10101010000010100
        10101010001111100
        10101100001010100
        10101010001010100
        10101010001010100
        10101001001010100
        11101001001110100
        10101101001010100
        00001010000000100
        00001000000000100
        00001000000010100
        00001000000001100
         
        如果将其中的“0”用空格替换,则效果如下(因网页问题有所调整(就是将原来的1个空格替换成2个空格),字符外观一致,但与实际数据不一致。最好在记事本或notepad++中显示):
                                    1   
            1  1111    111111 
        11111    1            1   
        1  1  1    1            1   
        1  1  1  1          1  1   
        1  1  1  1      11111   
        1  1  11        1  1  1   
        1  1  1  1      1  1  1   
        1  1  1  1      1  1  1   
        1  1  1    1    1  1  1   
        111  1    1    111  1   
        1  1  11  1    1  1  1   
                1  1              1   
                1                  1   
                1              1  1   
                1                11   
        这个便是传说的“啊”字了。
        总结一下,汉字在计算机中是内码,内码-0xa0a0等于区位号(用十进制表示,就是第xx区yy位),内码-0x8080就是国标码(十六进制),而(区码-1)*94 + (位码-1)就是“字库码”——即这个汉字在字库文件中的偏移量。找到了这个偏移量,就可读取数据,显示出来了。

3、显示
        如何显示呢?从前面的演示应该有所体会,就是将读到的数据中,为1的就是某种字符打印,为0的就打印空格。为方便起见,统一使用星号(“*”)打印。这个打印当然是逐位打印了,就是对每个字节都进行判断。在下面代码中将会看到。
        完整的代码如下:
        /***************************************************
         字符集编码统一为gb2312,即源代码文件保存格式为gb2312(notepad++下显示为“ANSI”),
         编译环境的字符集编码为gb2312,如果不是,可能得不到预期效果
         多个汉字
         * ************************************************/
        
        #include<stdio.h>
        #include<unistd.h>
        #include<sys/stat.h>
        #include<sys/types.h>
        #include<fcntl.h>
        #include<stdlib.h>
        #include<errno.h>
        #include<string.h>
        
        /* for debug */
        #define DEBUG
        #define DEBUG
        #ifdef DEBUG
        #define debug(fmt, ...) printf(fmt, ##__VA_ARGS__)
        #else
        #define debug(fmt, ...)
        #endif
        #if 0
        /* 啊的一种 */
        unsigned char  a[] = {
        0x00,0x08,0x0F,0x7C,0xE9,0x08,0xAA,0x08,0xAC,0xE8,0xAA,0xA8,0xAA,0xA8,0xAA,0xA8,
        0xEA,0xE8,0xAE,0x08,0x08,0x08,0x08,0x08,0x08,0x28,0x08,0x10,0x00,0x00,
        };
        
        /* 啊的另一种 */
        unsigned char b[] = {
        0x00, 0x04, 0x2f, 0x7e, 0xf9, 0x04, 0xa9, 0x04, 0xaa, 0x14, 0xaa, 0x7c, 0xac, 0x54, 0xaa, 0x54,
        0xaa, 0x54, 0xa9, 0x54, 0xe9, 0x74, 0xad, 0x54, 0x0a, 0x04, 0x08, 0x04, 0x08, 0x14, 0x08, 0x0c,
        };
        #endif
        
        void display_font(char *mat)
        {
                int i, j, k;
                for(j=0;j<16;j++)
                {
                        for(i=0;i<2;i++) /* 一个汉字占两个字节 */
                        {
                                for(k=0;k<8;k++)
                                {
                                       /* 逐位相与,为1者打印 */
                                       if(mat[j*2+i] & (0x80>>k))
                                        //if(b[j*2+i] & (0x80>>k)) // 测试HZK16文件中的二进制 
                                                printf("*");
                                        else
                                                printf(" ");
                                }
                        }
                        printf("\n");
        
                }
        }
        int main()
        {
                int i;
                unsigned char incode[] = "啊";         /* 字符集编码为gb2312下可显示该汉字 */
                unsigned char qh,wh;
                unsigned long offset;
                FILE    *HZK;
                char *mat;
        
                if((HZK=fopen("HZK16","rb"))==NULL)
                {
                        perror("Can't Open HZK16");
                        exit(0);
                }
        
                mat=(char *)malloc(32);
                memset(mat,0,32);
        
                for (i = 0; i< sizeof(incode)-1; i+=2)
                {
                        qh = incode   - 0xa0;
                        wh = incode[i+1] - 0xa0;
                        debug("code : %x %x\n", incode, incode[i+1]);
                        offset = ( 94*(qh-1) + (wh-1) ) * 32; /* 寻址 */
                        debug("code : %x %x %x\n", incode, incode[i+1], offset);
                        fseek(HZK,offset,SEEK_SET);
                        fread(mat,32,1,HZK);
                        display_font(mat);
                }
                free(mat);
                fclose(HZK);
                return 0;
        }

代码运行效果:
        $ ./a.out           
        code : b0 a1
        code : b0 a1 b040
                                  *   
            *  ****  ****** 
        *****    *          *   
        *  *  *    *          *   
        *  *  *  *        *  *   
        *  *  *  *    *****   
        *  *  **      *  *  *   
        *  *  *  *    *  *  *   
        *  *  *  *    *  *  *   
        *  *  *    *  *  *  *   
        ***  *    *  ***  *   
        *  *  **  *  *  *  *   
                *  *            *   
                *                *   
                *            *  *   
                *              **

这个代码可以打印多个汉字,当使用如下赋值语句时
        unsigned char incode[] = "我顶";
        打印效果如下:
                        $ ./a.out           
                code : ce d2
                code : ce d2 216e0
                        *    *             
                        ***  *  *         
                  ****      *    *       
                        *      *    *       
                        *      *        *   
                *************** 
                        *      *             
                        *      *    *       
                        *  *  *    *       
                        **      **         
                      **        *           
                  **  *      *  *         
                        *    *    *         
                        *  *        *  *   
                    *  *            *  *   
                      *                **   
                code : b6 a5
                code : b6 a5 f740
                                  *   
                *  ********* 
        ******        *         
              *          *      *   
              *      ******** 
              *      *          *   
              *      *    *    *   
              *      *    *    *   
              *      *    *    *   
              *      *    *    *   
              *      *    *    *   
              *      *    *    *   
              *          *           
          *  *          *  **     
            *          *        *   
                    **            *

既然能显示汉字了,那么能不能将中文、英文放在一起,混合显示呢?答案是肯定的。从GB2312简体中文编码表可以看到,这种编码是从A1A0开始的,与ASCII码完全没有交集。就是说,先判断所要显示的字符,如果大于0xa0,肯定是GB2312里面的,小于应该就是ASCII码了(仅考虑GB2312编码及ASCII码,其它不涉及)。那么程序就应该比较简单了。不过代码中有一个严重的bug,因为代码是按两个字节进行处理的,如果连续出现的英文字符不是2的倍数,其后的字符就乱了。现在就不拿出来献丑了,等解决了,再拿出来。

参考资料:
        1、一些概念性的东西,可以自行搜索,如“内码”、“区位码”、“点阵字库”、“矢量字库”等等。
        2、CU的一个帖子,本次研究主要以此为基础:http://bbs.chinaunix.net/thread-1997845-1-1.html。
        3、汉字字库文件,如HZK16等等,直接使用搜索引擎搜索“HZK16”,应该有下载链接的。
        4、关于计算机编码详细资料,可以搜索“信息传递、编码和计算机表示”系列文章。如http://blog.csdn.net/FreeWave/archive/2010/04/13/5482359.aspx讲述了GB2312编码。

转载自:迟思堂工作室

转载地址:http://www.tianyiic.com/thread-34-1-1.html

点阵字库显示系列之二:GB2312点阵字库显示相关推荐

  1. 点阵字体显示系列之二:汉字显示

    http://blog.csdn.net/subfate/article/details/6444582 免责声明: 本文是作者在研究过程中的一篇文章,本着互联网共享.自由(free,应该不是&quo ...

  2. 点阵字体显示系列补记:将字库文件转换成数组形式

    昨天写完几篇文章后觉得意犹未尽,我想想了,既然字库文件是二进制文件,完全可以转化为十六进制,存储在数组中,这样在寻找字符时就不用操作文件了,直接在内存中获取. 经过一番调研,证明这个思路是对的,是具有 ...

  3. C# 转换TTF为GB2312点阵字库

    C# 转换TTF为GB2312点阵字库 前段时间碰到的一个项目要求在LCD上显示收到的短信,需要12×12的GB2312点阵字库.因为板子上已经放了块W25Q64了,就想着找个字库烧进去.最后虽然是用 ...

  4. 点阵字体显示系列之三:使用ncurses显示汉字

    ncurses这个库,最早听说应该是当年刚接触Linux的时候,当时,我们宿舍就一个人在鼓捣Linux,他是我们后来的班长,如今在ZLG混,也不知混得怎么样了.我也不知道哪条神经线路出现故障了,竟然傻 ...

  5. 51单片机点阵和取字模软件的使用方法(显示心形图案)

    点阵 首先来介绍一下8*8 点阵共由 64 个发光二极管组成,且每个发光二极管是放置在行线和列线的交叉点上,当对应的某一行置 1 电平,某一列置 0 电平,则相应的二极管就亮: 如要将第一个点点亮,则 ...

  6. DJYGUI系列文章四:GK文本显示

    目录 1 GK文本显示概述 1.1 ansi系 1.2 unicode系 1.3 DJYGUI文本显示 2 字符集说明 3 字符集API说明 3.1 ModuleInstall_Charset:字符编 ...

  7. nginx系列之二:配置文件解读

    ** 前言 ** nginx系列之一:nginx入门 nginx系列之二:配置文件解读 nginx系列之三:日志配置 nginx系列之四:web服务器 nginx系列之五: 负载均衡 nginx系列之 ...

  8. 《视频直播技术详解》系列之二:采集

    七牛云于 6 月底发布了一个针对视频直播的实时流网络 LiveNet 和完整的直播云解决方案,很多开发者对这个网络和解决方案的细节和使用场景非常感兴趣. 结合七牛实时流网络 LiveNet 和直播云解 ...

  9. 【点阵液晶编程连载三/B】点阵LCD 的驱动与显控

    3.6. 字符显示原理 3.6.1. 字符与字模 驱动程序当中,字符库(也就是字模的集全)的数据采用了与一般的单色点阵LCD 的数据组成方式,即字模当中的一个位代表LCD 显示中的一个像素点,取点方式 ...

最新文章

  1. GPT-3等三篇论文获NeurIPS 2020 最佳论文奖!华人一作获时间检验奖
  2. python爬取图片-Python超简单的爬取网站中图片
  3. centos6 yum修改源
  4. 反思~我们是否应当克制对新技术的追求?
  5. 智能判断图片中是否存在某物体_智能家居组件漫谈——人体传感器
  6. Dapr微服务应用开发系列5:发布订阅构建块
  7. P7405-[JOI 2021 Final]雪玉【二分】
  8. .典型用户 - 场景
  9. maven仓库理解、下载及设置
  10. mybatis中经典的9种设计模式
  11. erp无线架设服务器,erp数据库架设在云服务器上
  12. Python内置函数之 range()
  13. 深度学习与NLP简单应用
  14. jdk32位安装包下载_premiere pro 2017 软件下载及安装教程
  15. ubuntu解决软件下载速度过慢
  16. win7开机rpc服务器不可用进不了系统,win7系统提示rpc服务器不可用怎么解决
  17. 【数据结构】范浩强Treap(非旋转平衡树)可持久化Treap总结
  18. 盒子模型(标准盒模型、怪异盒模型)
  19. Ubuntu/Linux Server 服务器系统安装
  20. 【黑金ZYNQ7000系列原创视频教程】07.自定义IP——定制RTC IP实验

热门文章

  1. 计量经济学导论伍德里奇第六版答案+数据集
  2. 协程大批量爬取是要被封IP的,最优秀的方法就是在被封IP时候立马切换IP
  3. Python txt转pcd(带RGB值,点云)
  4. iOS自动化打包发布(fastlane)
  5. 图解经典电路之OCL差分功放-三极管分立器件电路分析
  6. openstack和云桌面杂记
  7. 内存模块与计算机兼容,内存条不兼容怎么办?常见解决方法在此!
  8. java 切图_java用pdfbox切图并重绘宽高
  9. 你了解企业内部的往来款会计分录怎么做吗?
  10. 快速求sin与cos值的方法