C语言汉字编码 梳理

一、关于汉字编码和字库

根据汉字使用频率,正常使用的汉字约15000个,根据频率可分为高频字、常用字、次常用字、罕见字和死字。我国1981年发布《通讯用汉字字符集(基本集)及其交换码标准》GB2312-80方案,将高频字、常用字、次常用字编成汉字基本字符集共6763个。又在该字符集中使用频率,分成3755个按拼音排序的一级汉字和3008个按部首排序的二级汉字,另外编码收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。

二、汉字编码

区位码——分区表示

国家标准的汉字字符集GB2312-80,即国标,对收录字符进行分区管理:将字库分成94个区,每个区有94个汉字(按位编排),每一个汉字在字库中有确定的区和位编号,即所谓的两个字节表示的区位码,区位码第一个字节表示区号,第二个字节表示位号,由区位码即可获取汉字在字库中的地址。其中:

  • 01-09区收录除汉字外的682个字符
  • 10-15区为空白区,没有使用
  • 16-55区收录3755个一级汉字,按拼音排序
  • 56-87区收录3008个二级汉字,按部首/笔画排序
  • 88-94区为空白区,没有使用
  • 例:“啊”是GB2312中的第一个汉字,区位码是1601

每个汉字在字库中以点阵字模形式存储,一般为16×16点阵形式。每个点用一个二进制位表示,存1的点,可以在屏幕上显示一个亮点,存0的点则不显示,存字的16*16点阵信息在显示器上显示,即可出现对应汉字。

国标码

GB2312规定对收录的每个字符采用两个字节表示,第一个字节为“高字节”,对应94个区,第二个字节为“低字节”,对应94个位,所以其编码范围是:0101-9494。GB2312为中文编码,但是也会用到英文字母等字符,为了兼容,需避开ASCII中不可显示字符0000 0000-0001 1111(十六进制0-1F,十进制0-31)及空格字符0010 0000 ,国标码规定汉字的表示范围为0010 0001,0010 0001-0111 1110,0111,1110(十六进制为2121-7E7E,十进制为3333-126-126),即分别将区码和位码加上20H(十六进制数,代表十进制中的32,H为十六进制数的后缀:Hexadecimal)。即国标码就是区位码向后偏移32,以避免和ASCII码中0-32的不可显示字符和空格字符相冲突的结果。

内码/机内码

国标码虽然向后偏移20H,但是还是会和ASCII码中的除控制字符外的其他字符冲突,导致乱码,为解决这个问题,规定把国标码中的每个字节的最高位从0换成1,相当于每个字节再加上128(十六进制80H,二进制1000 0000)即得到所谓的机内码,即内码,ASCII码只用了一个字节中的低7位,最高位为0,所以最高位为1就可以作为区别汉字编码和ASCII码的标志。

总结一下:编码范围

综上所述,内码才是字符用GB2312编码后在计算机中存储的形式。

GB2312规定对收录的每个字符采用两个字节表示,第一个字节为“高字节”,对应94个区,第二个字节为“低字节”,对应94个位,所以其编码范围是:0101-9494.区号和位号分别加上0xA0就是GB2312编码。例:最后一个码位是9494,区号和位号分别转成16进制是5E5E,0x5E+0xA0=0xFE,即该码位的GB2312编码是FEFE。

编码范围:

  • GB2312内码范围:A1A1-FEFE
  • 其中汉字的编码范围是B0A1-F7FE
  • 第一字节0xB0-OxF7,对应区号16-87
  • 第二个字节0xA1-0xFE,对应位号01-94

解释:一个字节对应8位,8位的数可以表示的范围为0-256,但是这里的范围是1-94,规定,当区号和位号分别先后加上0x20、0x80,即总共加上0xA0后就是GB2312对应的机内码。对于区号和位号的组合体,0101-9494,加上一个0xA0十六进制数变成GB2312编码:高字节的最小数1转换成:1+0xA0=0x01+0xA0=0xA1,高字节最大数94转换成:94+0xA0=0x5E+0xA0=0xFE,即可求是GB2312编码范围:A1A1=FEFE。

区位码+32(0x20)=国标码
国标码+128(0x80)=机内码
->区位码+160(0xA0)=机内码

三、20H和80H

为什么加上20H:

不可/可打印字符:ASCII中的可打印字符即英文字母/数字/和符号部分(33-126,127为不可打印的DEL),其余的为控制字符等不可打印字符

全角字符/半角字符:制定GB2312时,决定把ASCII中的可打印字符重新编入GB2312中,以两个字节表示,称为全角字符,全角字符在屏幕上显示的空度为ASCII字符的两倍,对应的ASCII字符也因此被称为半角字符;

同时,对于前32个控制字符(ASCII码为0-31)以及第33个可显示但是不可打印的空格字符(ASCII码32)等共33个不可打印字符的ASCII编码直接沿用,不再重新编码,所以不能采用区码直接作为计算机处理的机内码,需要向后偏移32以避开和前33个字符的冲突。因为区码中的区码和位码都是从1开始计数,ASCII码是从0开始计数,所以向后偏移32而不是33.这也就是最终版本的国标码

为什么加上80H:

直接采用国标码作为计算机直接处理的机内码时,因为没有避开ASCII码中的英文字母、数字和其他符号(33-126,共94个字符,127为不可打印的DEL),若用ASCII码编码的英文字符在GB2312编码的环境中则同样会发生乱码,即国标码并没有完全兼容ASCII码。考虑到ASCII码只使用了一个字节中的低7位,最高位为0,国标码两个字节中的最高位原本都恒为0,即国标码中的每个字节实际上也只用了一个字节中的低7位。于是决定将国标码的每个字节的最高位设为1,即形成了GB2312的机内码,即内码,简称GB2312码。即国标码要再加上80H才变成机内码。

四、外码/输入码/输入法编码

是用来将汉字输入到计算机中的一组键盘符号,是作为汉字输入用的编码。是汉字系统的输入体系,使汉字与键盘建立对应关系。主要有以下几类

  • 数字编码:如区位码
  • 拼音编码,如全拼/双拼/自然拼
  • 字形编码:如五笔/表形码/郑码等

字形码/字型码/输出码

把汉字按图形设计成点阵图,就得到了相应的点阵代码,即字形码。就是用0、1表示汉字的字形,将汉字放入n行n列共n2正方形点阵内,凡是笔画经过的方格值为1,其余为0.

显示一个汉字一般用16*16,24*24或48*48点阵。存储一个汉字所需占用的点阵字节空间:字节数=点阵行数*(点阵列数/8)。如用16*16点阵表示一个汉字,即将每个汉字用16行每行16个点表示,一个点需要1位二进制代码,共需16行*2字节/行=32个字节。

为了将汉字的字形显示或者打印出,汉字信息处理系统还配有汉字字形库,也称字模库/字库。按输出方式可分为显示字库和打印字库。显示字库用于显示输出,工作时需调入内存,打印字库用于打印输出无需调入内存。

汉字字模在字库中存放的位置根据汉字的区位码来确定,在支持汉字输入的系统中,键盘键入的汉字内码即在程序中存在,将其转换为区位码,再从字库中找到对应的汉字字模,用有关的操作和循环语句,对每个字节的每一位进行判断,像过滤一样,某位为1则按设置的颜色,用graphics.h中显示像素点的函数putpixel()在屏幕的相应位置画点,为0则不画,这样就可以按预设的颜色在相应位置显示出汉字。

例:

①内码到区位码的转换
若汉字内码为十六进制数h2h1l2l1,则区号qh 相位号wh 分别为:
qh= h2h1-0xa0;
wh= l2l1-0xa0;
若用十进制表示内码为dld2,则
qh=dl-l60;
wh=d2-160;
即区位码qw 为:
qw=100*(d1-160)十(d2-160);
反过来,若已经知道了区位码qw。则也可求得区号和位号:
qh=qw/100;
wh=qw-100*qh;
②因而该汉字在汉字库中离起点的偏移位置(以字节为单位),可计算为:
offset=(94*(qh-1)+(wh-1))* 32;
③这样即可以找寻到文件的偏移量,读出一个char bytes[32]数组。这样bytes 数组中则
存了要显示汉字的16×16 点阵字模,然后将字模按行扫描的办法,通过循环用putpixel()函
数在屏幕设定位置显示出象点,因而组合成一个显示的汉字。

参考资料:

彻底解决乱码问题

国标2312编码

汉字机内码/国标码和区位码的区别

猜你喜欢

转载自www.cnblogs.com/yuanerduo/p/12534616.html
今日推荐