汉字内码漫谈(转载的,想用串口发汉字)

不用通常的汉字输入法能输入汉字吗?在英文状态下能打印出汉字吗?请看下面两个实验:
  实验一·启动UCDOS汉字系统,在“英文”输入状态下,将副键盘切换成数字输入状态,然后在系统提示符后面作如下操作:
  左手按住Alt,右手依次敲副键盘上的1、7、6,松开Alt键;再按住Alt键,依次敲副键盘上的1、6、1,松开Alt键,这时屏幕光标处出现了一个“啊”字。
  实验二·打开自带汉字库的打印机(如LQ150K,LQ1600K等),启动DOS(不启动任何汉字系统),在系统提示符后面作如下操作:
  ①键入命令: COPY CON PRN←┘
  ②同实验一中输入“啊”的操作;
  ③敲回车键,再敲F6键,再敲回车键。
  这时打印机打印出一个“啊”字。
  在实验一中,我们在汉字系统的英文输入状态(注意,不是在拼音、五笔字型等汉字输入状态)下输入了汉字;实验二中,在英文状态下打印出了汉字。道理何在呢?
  原来,计算机上使用的汉字有两类代码,一类叫外码,用来输入汉字,如拼音码、郑码、五笔字型码等。由于人们不断寻求更佳的汉字输入法,因此外码也就层出不穷。不同的外码规则也不同,如果计算机内部存储汉字时,也采用这些五花八门的编码,势必使汉字系统过于复杂。因此,不论用什么输入法输入的汉字,在存入存储器时,都将它的外码转换成一种统一的代码,这就是汉字内码。
  一个汉字的内码由两个字节组成。汉字内码与区位码之间有一个简单的数学关系:
  内码第一字节=区码+160
  内码第二字节=位码+160
  比如,查区位码表知道,“啊”字在16区01位,它的内码为:
  第一字节=16+160=176
  第二字节=1+160=161
  这下就明白了,在前面的两个实验中,我们是利用Alt键加副键盘能输入字符代码的功能,直接用内码输入了汉字“啊”。
  我们知道,英文字符的代码ASCII只用一个字节表示。为什么一个汉字要用两个字节来表示呢?原来一个字节(8位二进制数)能表示的最大整数范围是0~255,也就是说最多能表示256种不同的状态,这用于表示几十个英文字符足够了。但是汉字有成千上万个,所以至少要用两个字节(16位二进制数)来编码。两个字节最多可有65536种不同的编码。
  由于大多英文软件只处理单字节对象,而汉字内码又是双字节的。在汉字系统中使用英文软件来处理汉字时就会带来一些问题。比如我们用DOS 6.2中的EDIT编辑一个文本文件,要删掉其中的一个汉字时,要敲两次删除键才能删掉。若是只删了一下,后面的汉字就变得面目全非了。道理很简单:删一下,只删掉了汉字内码的一个字节,剩下的一个字节和下一个汉字的头一个节就构成了另一个汉字或符号的内码,于是就显示出和这个内码对应的汉字或符号。依次错位,后面的汉字就全变了。
  在汉字系统中使用英文软件还会带来另一个问题:由于在原来的英文系统中规定大于127的代码用来代表一些特殊符号,如英文的制表符、欧洲其它文字的字母等。而汉字的内码的每个字节的值都大于127,这样,即使是在汉字系统中,计算机在遇到汉字内码时可能会分不清是汉字还是其它特殊符号。这是我们在汉字系统中运行有些英文软件时会出现怪字或怪符号的原因。目前较流行的汉字系统都在努力改进,力争能分辨出汉字内码和特殊字符的代码,但仍难免疏漏。你可作一个实验:在汉字系统(如常用的UCDOS)中执行DOS命令TREE,在显示出来的结果中就会出现怪字,解决的方法很简单:暂时转到英文显示状态(不用退出汉字系统,如在UCDOS中按Ctrl-F7即可),原来英文系统中的特殊符号就不会被错认成汉字了。
  明白上述道理,也不难理解为什么在英文状态下显示的汉字是全是一些如“天书”的怪符号。因为在英文状态下所有的字符代码均不会被认为是汉字的内码,其值大于127的汉字内码都被当成特殊字符了。
  顺便说一下,我们常用的汉字系统的汉字内码为国标码(或异型国标码),而港台地区流行的汉字系统则采用的是另一种汉字内码──BIG-5码,由于内码不同,虽然都是汉字系统,却互不兼容。为此,一些两种内码转换的工具程序应运而生,如UCDOS中的实用程序CONVERT。但由于国标码与BIG-5码定义的汉字并非──对应的关系,因此这些转换程序不能百分之百地转换所有汉字,有些还得靠手工辅助完成。
  最后给出两个应用汉字内码的BASIC小程序。
  程序一利用区位码与内码的关系,输出区位码表的任意部分。这个程序中倒数第四行是关键的一句,其中的CHR$(i + 160) +CHR$(j + 160)将第i区第j位的汉字转换成汉字内码并以连续两个字节输出一个汉字。
  程序二将一串数字转换成全角的数字字符。区位码表中的全角符号与汉字只是所在的区不同,而内码规则是完全相同的。全角的数字字符“0”的区位码是0316、“1”的区位码是0317……,程序中的第三行正是据此将半角数字的ASCII码转换成汉字内码输出。
  这两个程序在QBASIC、True BASIC、Turbo BASIC中都能通过,运行时可在UCDOS等汉字系统中进行。
  
  程序一清单
  REM 打印区位码表程序
  PRINT
  INPUT "请输入起始区号,结束区号"; qu1, qu2
  INPUT "请输入起始位号,结束位号"; wei1, wei2
  REM 打印位号
  PRINT " ";
  FOR i = wei1 TO wei2
   PRINT " ";
   PRINT USING "##"; i;
  NEXT i
  PRINT
  REM打印区位码表(局部或全部)
  FOR i = qu1 TO qu2
   PRINT USING "##"; i;
   PRINT " ";
   FOR j = wei1 TO wei2
    PRINT CHR$(i + 160) + CHR$(j + 160) + " ";
   NEXT j
   PRINT
  NEXT I
  
  程序二清单
  INPUT "请输入一串数字"; n$
  FOR i = 1 TO LEN(n$)
   PRINT CHR$(3 + 160) + CHR$(ASC(MID$(n$, i, 1)) - ASC("0") + 16 + 160);
  NEXT i





C程序实现汉字内码与GB码
// HZEncode.cpp : Defines the entry point for the console application.

  //

  /*

  参考文献:

  汉字的编码和表示

  1)汉字交换码(国标码) 汉字交换码(国标码)主要用于汉字信息交换。

  国标码:以国家标准局1980年颁布的《信息交换用汉字编码字符集"基本集》(代号为GB2312 80)规定的汉字交换码作为国家标准汉字编码。 GB2312 80中共有7445个字符符号: 汉字符号6763个 一级汉字3755个(按汉语拼音字母顺序排列) 二级汉字3008个(按部首笔划顺序排列) 非汉字符号682个 GB2312 80规定,所有的国标码汉字及符号组成一个94 94的方阵。在此方阵中,每一行称为一个"区",每一列称为一个"位"。这个方阵实际上组成一个有94个区(编号由01到94),每个区有94个位(编号由01到94)的汉字字符集。 一个汉字所在的区号和位号的组合就构成了该汉字的"区位码"。其中,高两位为区号,低两位为位号。这样区位码可以唯一地确定某一汉字或字符;反之,任何一个汉字或符号都对应一个唯一的区位码,没有重码。

  区位码分布情况如下:

  区 号 内 容 1区 键盘上没有的各种符号 2区 各种序号 3区 键盘上的各种符号(按中文方式给出) 4 -5区 日文字母 6区 希腊字母 7区 俄文字母 8区 标识拼音声调的母音及拼音字母名称 9区 制表符号 10- 15区 未用 16-55区 一级汉字(按拼音字母顺序排列) 56- 87区 二级汉字(按部首笔划顺序排列) 88- 94区 自定义汉字

  由上可以看出,所有汉字与符号的94个区,可以分为四个组:

  ①1 -15区:为图形符号区。其中1 9区为标准符号区;10 15区为自定义符号区。

  ②16 -55区:为一级汉字区,包含3755个汉字。这些区中的汉字按汉语拼音顺序排序,同音字按笔画顺序列出。

  ③56 -87区:为二级汉字区,包含3008个汉字。这些区中的汉字是按部首笔划顺序排序的。

  ④88 -94区:为自定义汉字区。

  国标码规定,每个汉字(包括非汉字的一些符号)由2字节代码表示。每个字节的最高位为0,只使用低7位,而低7位的编码中又有34个适用于控制用的,这样每个字节只有27 - 34 = 94个编码用于汉字。2个字节就有94 94=8836个汉字编码。在表示一个汉字的2个字节中,高字节对应编码表中的行号,称为区号;低字节对应编码表中的列号,称为位号。

  汉字国标码的范围用二进制表示是: 00100001 00100001 01111110 01111110 (1+32)10 (1+32)10 (94+32)10 (94+32)10 7 位ASCII码是128个字符组成的字符集。其中编码值0 31(00000000 00011111)不对应任何印刷字符,通常称为控制符,用于计算机通信中的通信控制或对计算机设备的功能控制。编码值32(00100000)是空格字符SP。编码值127(1111111)是删除字符DEL。

  汉字国标码的起始二进制位置选择00100001即(33)10是为了跳过ASCII码的32个控制字符和空格字符。所以,汉字国标码的高位和低位分别比对应的区位码大(32)10或(00100000)2或(20)H,即: 国标码高位 = 区码 + 20H (H表示十六进制) 国标码低位 = 位码 + 20H

  2) 汉字机内码(内码)(汉字存储码)

  汉字机内码(内码)(汉字存储码)的作用是统一了各种不同的汉字输入码在计算机内部的表示。 为了将汉字的各种输入码在计算机内部统一起来,就有了专用于计算机内部存储汉字使用的汉字机内码,用以将输入时使用的多种汉字输入码统一转换成汉字机内码进行存储,以方便机内的汉字处理 汉字机内码是在计算机内部存储、处理的代码。计算机既要处理汉字,又要处理英文。因此计算机必须能区别汉字字符和英文字符。英文字符的的机内码是最高为为0的8位ASCII码。为了不与7位ASCII码发生冲突,把国标码每个字节的最高位由0改为1,其余位不变的编码作为汉字字符的机内码。

  汉字机内码的范围用二进制表示是: 10100001 10100001 11111110 11111110 机内码的高位和低位比对应的国标码的高位和低位大(128)10或(10000000)2或(80)H 即: 机内码高位 = 国标码高位 + 80H 机内码低位 = 国标码低位 + 80H 又因为: 国标码高位 = 区码 + 20H 国标码低位 = 位码 + 20H 所以: 机内码高位 = 区码 + A0H 机内码低位 = 位码 + A0H 也就是说,机内码高位和机内码低位分别比对应的区码和位码大(160)10或(10100000)2或 (A0)H 例如:汉字"啊"的区位码为"1601",其中区码为(16)10或(10)H,位码为(01)10或(01)H。 则: 机内码高位 = 10H + A0H = B0H 机内码低位 = 01H + A0H = A1H 所以: 机内码= B0A1H

以下是引用片段:

<!--[if !supportEmptyParas]--> <!--[endif]-->


3) 汉字输入码(外码)

  汉字输入码(外码)是为了通过键盘字符把汉字输入计算机而设计的一种编码。 英文输入时,相输入什么字符便按什么键,输入码和机内码一致。汉字输入时,可能要按几个键才能输入一个汉字。 汉字输入方案有成百上千个,但是这千差万别的外码输入进计算机后都会转换成统一的内码。 汉字输入方案大致可分为以下4种类型:

  (1) 音码:如全拼、双拼、微软拼音等

  (2) 形码:如五笔字型、郑码、表形码等

  (3) 音形码:如智能ABC、自然码等

  (4) 数字码:如区位码、电报码等

  4) 汉字字形码(输出码)

  汉字字形码(输出码)用于汉字的显示和打印,是汉字字形的数字化信息。 汉字的内码是用数字代码来表示汉字,但是为了在输出时让人们看到汉字,就必须输出汉字的字形。在汉字系统中,一般采用点阵来表示字形。 16 *16汉字点阵示意 16 * 16点阵字形的字要使用32个字节(16 * 16/8= 32)存储,24 * 24点阵字形的字要使用72个字节(24 * 24/8=72)存储。

  一般来说,表现汉字时使用的点阵越大,则汉字字形的质量也越好,当然每个汉字点阵所需的存储量也越大。

  5) 汉字地址码

  汉字地址码是指汉字库(这里主要指整字形的点阵式字模库)中存储汉字字形信息的逻辑地址。在汉字库中,字形信息都是按一定顺序(大多数按标准汉字交换码中汉字的排列顺序)连续存放在存储介质上的,所以汉字地址码也大多是连续有序的,而且与汉字内码间有着简单的对应关系,以简化汉字内码到汉字地址码的转换。

以下是引用片段:
*/  
<!--[if !supportEmptyParas]--> <!--[endif]-->
#include "stdafx.h"
#include "HZEncode.h"
<!--[if !supportEmptyParas]--> <!--[endif]-->
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define UNICODE
#define _UNICODE
/
// The one and only application object
<!--[if !supportEmptyParas]--> <!--[endif]-->
CWinApp theApp;
<!--[if !supportEmptyParas]--> <!--[endif]-->
using namespace std;
unsigned short* ptr;
char* pszHZ = "啊";
byte bt[] = {0xc4,0xe3,0xBA,0xC3};//“你好”的机内码
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
       int nRetCode = 0;
<!--[if !supportEmptyParas]--> <!--[endif]-->
       // initialize MFC and print and error on failure
       if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
       {
              // TODO: change error code to suit your needs
              cerr << _T("Fatal Error: MFC initialization failed") << endl;
              nRetCode = 1;
       }
       else
       {
              for (int i = 16;i <= 55; i++)
              {
                     byte Temp[3];
                     Temp[2] = 0;
                     Temp[0] = i + 0xA0;
                     for (int j = 1;j < 94;j++)
                     {
                             
                            Temp[1] = j + 0xA0;
                            cout << (LPCTSTR) Temp;
                             
                     }
                     cout << endl;
              }
<!--[if !supportEmptyParas]--> <!--[endif]-->
       }
<!--[if !supportEmptyParas]--> <!--[endif]-->
       system("pause");
       return nRetCode;
}
<!--[if !supportEmptyParas]--> <!--[endif]-->
  
<!--[if !supportEmptyParas]--> <!--[endif]-->


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值