VB6单片机编程中的汉字处理

【VB6单片机编程中的汉字处理】在DOS时代 , 拥有一个华丽的汉字菜单几乎是每个高档中文应用程序必须的包装 。中文Windows操作系统的出现使得高级开发平台实现全中文的提示和界面非常容易和方便 。在一般的应用程序中已经很少需要去专门考虑汉字处理的问题 。

但是在许多工程控制和字符串处理的环境中 , 汉字字符的处理仍然有别于西文字符的处理 , 需要加以专门的考虑 。

一、VB6对汉字处理的支持

对汉字处理 , VB6主要是提供了一些输入法设置方面的支持 。它提供了一个ImeMode属性和ImeStatus函数来确定和设置输入法的状态 。而且此属性和方法只在VB6的东亚区版本中有效 。

IMEStatus函数返回一个整数 , 用来指定当前Windows的输入法(IME)方式 。

下面是中文地区的返回值:

常数 值 描述

vbIMENoOP 0 不安装IME(缺省)

vbIMEOn 1 打开IME

ImeMode属性返回或者设置被选定的对象的 IME (Input Method Editor , 输入方法编辑器)状态 。

可以先用ImeStatus函数来检测当前的输入状态 , 并用IMEMode来控制其输入状态 。

如Text1.IMEMode=0(预定值)、=1(切换为中文输入)、=2(切换为英文输入)

对于简体汉字和繁体汉字 IME , 只能使用设置 0 到 2 。设置 3 到 10 对于汉字系统是无效的 。

VB6功能上对汉字处理的直接支持也仅限于此 。对于汉字字符串的处理 , VB6并没有提供专门的函数和方法 。

二、确定汉字字符串的长度

各个版本的VB对汉字字符的长度定义并不相同 。在Windows 3.x中 , VB3中认为每个汉字的长度为2字节 。VB自从32位版本以後 , 不管中英文字 , 均以2 bytes来储存 , 这与中文WinArray5的汉字内核有关 。但是VB4以后 , VB认为每个汉字的长度为1 。这在处理包含汉字的字符串时带来很多的不便 。

由于Len、Left、Mid等字符串函数认为一个汉字和一个西文字符的长度都为1 , 因此处理汉字字符串时有一定的难度 。实现上必须要能把汉字按照两个字符(字节)的形式读出 。因此在截取汉字字符串的子串时需特别注意 。

由于汉字处理和汉字字模存储的特殊性 , 在许多情况下我们希望汉字字符的长度为2 , 英文字符为1 。由于VB5和VB6把一个汉字作为一个字符 。因此处理汉字时首先必须能正确判断汉字字符串的长度 。

在VB3或C++里 , 汉字的 ASCII码均大于零 , 而VB5和VB6中汉字的ASCII码小于0 。因此通过判断一个未知字符的ASCII码就可以判断该字符是否汉字 。

这里我们提供了两种方法来判断汉字字符串的长度 。

1、方法1

VB6中提供了LenB函数用于字符串中的字节数据 。如同在双字节字符集(DBCS)语言中一样 , LenB返回的是用于代表字符串的字节数 , 而不是返回字符串中字符的数量 。如为用户自定义类型 , LenB返回在内存中的大小 。

LenB(StrConv(Str1 , vbFormUnicode))

对要处理的字符串Str1 , 必须先使用StrConv函数把ANSI格式的Byte数组转换为字符串 , 否则直接使用LenB函数得到的结果比实际结果大 。

2、方法2

这里自定义了一个子函数CLen来计算汉字字符串的实际长度 。同时还能判断字符串中汉字的实际个数 。

Function CLen(HzStr$) as Integer

Static HzNum as Integer

L = len(HzStr$)

For n=1 to L

If Asc(mid$( HzStr$ , n , 1))<0 Then HzNum = HzNum + 1

Next n

Clen = L + HzNum

End Function

CLen函数中的静态变量HzNum返回字符串中实际汉字的数目 。

三、汉字字模读写和存储的机理

计算机是以编码的方式来处理和使用字符的 。西文字符采用一个字节表示 , 即ASCII码 , 一般只用七位来表示128个字符 , 而把最高位用作奇偶校验(或者不用) 。我国国标规定汉字用内码表示 , 内码为两个字节 。为了保证中西文兼容 , 也就是说汉字系统的内码必须同时允许ASCII码和汉字的使用 , 两者之间不应发生冲突 。目前规定每个字节只用七位 , 若两个字节的最高位均为1 , 则该字符为汉字 。

国标对汉字字库的结构作了统一规定 , 即将字库分成若干个区 , 每个区有Array4个汉字 , 每个汉字在字库中有确定的区和位 , 因此每个汉字各有一个区位码 , 知道了区位码也就相当于知道了汉字在字库中的位置 。由于汉字的内码与区位码有一定的关系 , 所以只要通过汉字的内码就可得到该汉字的区位码 , 也就得到了该汉字的字模 。

查找一个汉字字模数据的算法为:

汉字内码 -> 区位码 -> 记录号 -> 字模数据

一个16点阵汉字其字模数据共有32字节 , 可以看作是一条记录 , 在程序中可以用一个数组存放 。在DOS的图形模式下 , 汉字是通过描点的方法逐点画上去的 。读取字模中每个字节的每一位 , 就能确定汉字中的每个点 。故一个16×16点阵的汉字必须要32个字节的字模数据才能确定 。

以下是DOS的图形模式下显示一个16×16点阵汉字时的描点顺序图 。每两个字节的字模数据确定一行 。

四、VB6中实现汉字字模转换的技巧

在许多工程控制的应用环境中 , 经常需要对标准的汉字字模进行调整和转换 。

要对汉字字模进行转换首先应该正确地读出16点阵汉字在字库中的32字节的字模数据 。设某一汉字的内码为ddff , 其中dd表示区内码 , ff表示位内码 , 则dd-&Ha1为该汉字的区码 , ff-&Ha1为该汉字的位码 。则该汉字在字库中的位置为:

Location = [(dd-&Ha1)×Array4 + (ff-&Ha1)]×32

需要注意的是 , 以何种方式从字库文件中读取这32字节也是一个关键问题 。由于二进制(Binary)方式访问文件可以直接查看文件中指定的字节 , 而且二进制方式也是唯一支持用户到文件的任何位置读写任意长度数据的方法 。因而以二进制方式打开汉字字库文件是最适合的 。

VB6虽然提供了较强的位运算功能 , 但是对于在字模转换中使用较多的移位操作 , 却没有提供对应的移位运算符、指令或函数 。其实通过and(与)、or(或)二个位运算符即可编制一个自定义子函数来实现移位运算 。

下面的自定义子函数就是实现循环右移的:

Public Function byteRight(byte1 As Byte, n As Integer) As Byte ’将byte1右移n位

Dim TemVar As Byte ’临时变量

Dim TemVar1 As Byte ’临时变量

Dim X, Y As Integer

TemVar = byte1

For X = 1 To n ’移多少位就循环多少次

For Y = 1 To 8 ’从第一位(右边第一位)开始循环右移

select Case Y

Case 1

If (TemVar And &H1) = &H1 Then ’如果临时变量TemVar的第一位是1,

TemVar1 = &H1 ’则将临时变量TemVar1置1,

Else

TemVar1 = &H0 ’则将临时变量TemVar1置0,

End If

Case 2

If (TemVar And &H2) = &H2 Then ’如果临时变量TemVar的第二位是1,

TemVar = TemVar Or &H1 ’则将其第一位置1(其它位不变),

Else

TemVar = TemVar And &HFE ’反之将第一位置0(其它位不变)

End If

Case 3

If (TemVar And &H4) = &H4 Then ’操作与上面相同

TemVar = TemVar Or &H2

Else

TemVar = TemVar And &HFD

End If

Case 4

If (TemVar And &H8) = &H8 Then

TemVar = TemVar Or &H4

Else

TemVar = TemVar And &HFB

End If

Case 5

If (TemVar And &H10) = &H10 Then

TemVar = TemVar Or &H8

Else

TemVar = TemVar And &HF7

End If

Case 6

If (TemVar And &H20) = &H20 Then

TemVar = TemVar Or &H10

Else

TemVar = TemVar And &HEF

End If

Case 7

If (TemVar And &H40) = &H40 Then

TemVar = TemVar Or &H20

Else

TemVar = TemVar And &HDF

End If

Case 8

If (TemVar And &H80) = &H80 Then

TemVar = TemVar Or &H40

Else

TemVar = TemVar And &HBF

End If

If TemVar1 = &H1 Then ’移完第八位后,如果TemVar1是1(即第一位是1)

TemVar = TemVar Or &H80 ’则将TemVar的第八位置1

Else

TemVar = TemVar And &H7F ’反之置0

End If

End select

Next Y

Next X

byteRight = TemVar ’将TemVar的值返回给函数名

End Function

尤其需要注意的是当把二进制数据写入文件中时 , 必须使用Byte数据类型的数组变量 , 而不是 String 变量 。String 被认为包含的是字符 , 而二进制型数据可能无法正确地存在 String 变量中 。

五、一个实际应用案例

图形点阵液晶在现代单片机系统中是一种十分常用的显示设备 , BP机、手机上的显示屏就是图形点阵液晶 。它能显示汉字和图形 , 与行列式键盘组成了单片机系统中最常用的人机交互界面 。但是直接从中文系统汉字字库中提取的汉字字模并不能直接在液晶上显示 , 通常都必须经过格式上的调整和转换 。

1、图形点阵液晶的汉字字模

与在西文DOS中显示汉字不同的是 , 图形点阵液晶并不是简单地用画点的方式来描出汉字 。以常用的HD61202图形点阵液晶显示控制模块为例 , 它能控制 64×64点阵液晶的显示 , 其显示RAM共64行 , 分8页 , 每页8行 , 每一页的数据寄存器分别对应液晶屏幕上的8行点 , 对显示RAM的一个字节单位赋值就是对当前列的8行(一页)的像素点是否显示进行控制 。连续16列和相邻的2页的32字节显示RAM就可以控制一个汉字的显示区域 。对这些显示RAM赋以相应的值就可以显示出一个汉字 。

HD61202图形点阵液晶显示控制模块的汉字字模的排列实际上是标准汉字字模排列形式旋转而成的 。对标准汉字字模转换的目的就是在单片机系统的数据存储器中(如E2PROM)存储经过调整的连续32字节的16进制数 。

2、实际源程序

以下这段程序是放置在汉字源文本输入框(SrcTxt)的Change事件中 。通过判断输入在文本框内的字符的ASCII码是否小于零 , 就能判断输入的字符是不是汉字 。这段程序还能计算汉字字符串的长度 。同时把输入的汉字存储在一个临时文件TempSrc.txt中 。由于这段代码是放在文本框的 Change事件中 , 它能立即更新汉字个数的显示 。

Private Sub SrcTxt_Change( )

Static SStr As String

Dim i As Integer

Dim TempFile, TempFileBinary As String

TotalNum = 0

L = Len(SrcTxt.Text)

For i = 1 To L

tmpStr = StrConv(Mid$(SrcTxt.Text, i, 1), vbWide)

If Asc(Mid$(SrcTxt.Text, i, 1)) < 0 Then

TotalNum = TotalNum + 1

SStr = SrcTxt.Text

Else

MsgBox "写入的不是汉字!"

SrcTxt.Text = Left(SrcTxt.Text, Len(SrcTxt.Text) - 1)

Exit Sub

End If

Next i

LblNum.Caption = Str$(TotalNum) + "个汉字"

TempFile = App.Path + "\" + "TempSrc.txt"

’TempFileBinary = App.Path + "\" + "TempSrcBinary.txt"

Open TempFile For Output As #1

Print #1, SrcTxt.Text

Close #1

End Sub

在实例中选用了UCDOS 5.0汉字系统中的16点阵字库Hzk16作为提取汉字字模的标准字库 。

Private Sub CmdCnt_Click( )

Dim TempSrcFile As String

Dim TempDestFile As String

Dim TempFile As String

Dim HzFile As String

Dim To61202(32) As Integer

Dim p(1 To 2) As Byte

Dim C1, C2

Dim rec As Integer

Dim Location As Long ’汉字在字库中的位置

Dim Hz(0 To 31) As Byte ’转换完的32字节的字模数据

Dim Buf1(0 To 31) As Byte ’暂存转换过程中的32字节字模数据

Dim HzAll( ) As Byte ’存放全部字模数据的动态数组

Dim LoopAll As Integer

Dim bit, k2, k3 As Byte

Dim i, j, i1, k, k1, k4, k5, k6 As Integer

DestTxt.Text = "" ’DestTxt是目标文本框 , 存放转换后的16进制数据

Flag = 0

TempDestFile$ = App.Path + "\" + "TempDest.txt"

If FileExists(TempDestFile$) Then Kill TempDestFile ’FileExists是一个检查文件是否存在的自定义函数

If SrcTxt.Text = "" Then ’汉字输入框内无汉字则退出

MsgBox "没有可以转换的字模源文件!"

Exit Sub

End If

HzNum = Len(SrcTxt.Text) ’获得汉字的个数

ReDim HzAll(0 To HzNum * 32 - 1) ’重新定义动态数组的上界

Open TempFile For Output As #1

Print #1, SrcTxt.Text

Close #1

For LoopAll = 0 To HzNum - 1

Open TempFile For Binary Access Read As #1 ’按二进制方式打开

Get #1, 2 * LoopAll + 1, p

Close #1

C1 = CStr(p(1)) - &Ha1 ’区内码

C2 = CStr(p(2)) - &Ha1 ’位内码

rec = C1 * Array4 + C2

Location = CLng(rec) * 32 + 1 ’该汉字在16*16点阵字库中字模第一个字节的位置

HzFile = App.Path + "\" + "hzk16"

Open HzFile For Binary Access Read As #1 ’读取该汉字在16点阵字库中的原始字模

Get #1 ,  Location ,  Hz

Close #1

’以下是将UCDOS字库的存储格式调整为HD61202的规范格式

For j = 0 To 3

If j = 0 Then k4 = 14

If j = 1 Then k4 = 15

If j = 2 Then k4 = 30

If j = 3 Then k4 = 31

For k = 0 To 7

bit = &H80

bit = byteRight((bit), (k))

For i = 0 To 7

k2 = byteleft(Buf1(j * 8 + k), 1) ’整个流程是由低位向高位移动,最后凑成一个字节

k3 = byteRight((Hz(k4 - i * 2) And bit), 7 - k) ’将字节中的某位移到最低位

k3 = k3 And &H1 ’屏蔽掉其余7位

Buf1(j * 8 + k) = k2 Or k3

Next i

Next k

Next j

For i1 = 0 To 31 ’将调整后的汉字字模再装入原数组

Hz(i1) = Buf1(i1)

HzAll(LoopAll * 32 + i1) = Buf1(i1)

Next

Next LoopAll

Open TempDestFile For Binary Access Write As #1 ’转换结果保存到TempDestFile中

Put #1, 1, HzAll

Close #1

MsgBox "OK!"

End Sub

以上程序均在中文VB6专业版上调试通过 。

以上程序在实用中取得了很好的效果 。此汉字字模转换程序丰富了单片机系统开发工具的功能 , 是包含液晶显示功能的单片机系统在系统调试和开发过程中不可或缺的功能模块 。

    推荐阅读