主题:打开、保存Unicode或UFT-8编码文件的源代码
打开、保存Unicode或UFT-8编码文件的源代码
1.Unicode编码文件的读写技术简述
Unicode编码是两字节的全编码,对于Ascii字符它也使用两字节表示,代码页通过高字节的取值范围来确定是Ascii字符,还是汉字的高字节。
Unicode编码的文本文件开头两个字节是 &HFF 和 &HFE,这是Unicode头,即Unicode编码文件标记。下面就简单介绍如何在自编的文本编辑器中实现Unicode编码文本文件的读写。
我们知道VB的字符串在内存中实际上就是以Unicode编码形式保存的,但当程序将字符串写入txt文件时,已经自动将之转变为 Ansi格式的编码形式,所以我们要将文本保存为Unicode编码的文件的话,就不能直接写入这个字符串,而应先将该字符串赋值给一个Byte型的数组,再将这个数组原封不动地写入文件,并在文件开头添加Unicode头就行了。
读文件时,VB并不判断读入的数据开头两个字节是否为 &HFF 和&HFE,它将读入的数据都自动转变为Unicode编码的字符串,如果原来就是Unicode编码的文本,哈哈,这下子就弄得一踏糊涂了。所以我们在读文件的时候,不能用通常的字符读入法,而要用二进制读入法,先将数据读入到一个Byte型的数组中,再判断头两个字节是否 &HFF 和 &HFE,如果是,就把这个数组直接赋值给一个字符串变量,再去掉该字符串的第一个字符;如果不是,就通过StrConv函数转换即可。
2.UTF_8编码文件的读写技术简述
下面我先简单讲述一下UTF-8编码的有关知识,并且只讲与我编写的读写UTF-8文本源代码有关的知识(如有谬误之处请各位兄弟指正),详细的理论请各位自己找专业文章看看。
UTF-8编码字符理论上可以最多到6个字节长,但目前全世界的所有文字和符号种类加起来也只要编到4个字节长就够了。
UTF-8是以8位(即1个字节)为单元对原始码进行编码(注意一点:这里所讲的原始码都是指Unicode码),并规定:多字节码(2个字节以上才称为多字节)以转换后第1个字节起头的连续“1”的数目(这些连续“1”称为标记位),表示转换成几个字节:“110”连续两个“1”,表示转换结果为2个字节,“1110”表示3个字节,而“11110”则表示4个字节……跟随在标记位之后的“0”称为分隔位,其作用是分隔标记位和字符码位。第2~第4个字节的起头两个位固定设置为“10”,也作为标记,剩下的6个位才做为字符码位使用。
这样,2字节UTF-8码剩下11个字符码位,可用以转换0080~07FF的原始字符码,3字节剩下16个字符码位,可用以转换0800~FFFF的原始字符码,由此类推。编码方式的模板如下:
原始码(16进制) UTF-8编码(二进制)
--------------------------------------------
0000 - 007F 0xxxxxxx
0080 - 07FF 110xxxxx 10xxxxxx
0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx
……
--------------------------------------------
模板中的“x”表示字符码。
VB能识别的Ascii码<007F,所以在VB中,Ascii码都只能编为1个字节的UTF-8码。Unicode编码在0080-07FF的范围内,被编为2字节的UTF-8码,其中0080-00FF的编码VB不能显示,0100-07FF的编码是少数国家的文字,与汉字无关,所以,我们基本上不会遇到2字节的UTF-8编码。汉字的Unicode编码范围为0800-FFFF,所以被编为3字节的UTF-8码。
例如“汉”字的Unicode编码是6C49,6C49在0800-FFFF之间,所以要用3个字节的模板:
1110xxxx 10xxxxxx 10xxxxxx。
UTF-8编码文件有UTF8头,用 &HEF、&HBB 和 &HBF 3个字节标记。
要写入UTF-8编码的文本文件,关键是对汉字编码的处理。我们从上述的汉字编码模板就可以看出,对汉字的处理步骤大致为:
第一步:取得汉字的Unicode码。
第二步:将Unicode码分解为2个16进制数据。
第三步:将这2个16进制数据转换成二进制数据并连接。
第四步:将二进制数据分解为3个串,第1个串为4个位,在前面加上标记位“1110”,第2、3个串均为6个位,分别在前面加上“10”标记位。
第五步:将这3个串分别转换为10进制数据并赋值给字节型数组。
第六步:将字节型数组用二进制法写盘,并且要先写入UTF8头(&HEF、&HBB、&HBF),再将转换好的数据写入。
要读取UTF-8编码的文本文件,对汉字的处理步骤大致为:
第一步:用二进制法读入文本数据,赋值给字节型数组,并判断前3个字节是否UTF8头,如果是,才进行以下的处理。
第二步:逐个字节判断是否汉字编码,如果是,就再提取后2个数组元素,共3个数组元素来加以处理。
第三步:将这3个数据都转换成16进制数据。
第四步:将3个16进制数据都转换成二进制数据。
第五步:从第1个二进制数据中去掉前4位,从第2、3个二进制数据中分别去掉前2位,并将这3个处理后的二进制数据依次连接,成为1个16位的字串。
第六步:从这个二进制串中分别提取前8位和后8位转换成2个10进制数据,这2个数据就是汉字的Unicode码了,将它们赋值给1个字符型变量即可。
由于上述的二进制数字均须进行大量的字符串操作,速度较慢,因此在实际的代码中,笔者采用了逻辑运算(位操作)来代替上述的字符串操作。
新建一个工程,在窗体上添加3个按纽,1个文本框,文本框设置成可接受多行文本。
代码如下:
Option Explicit
Private Sub Command1_Click()
On Error GoTo InErr
Dim fName As String, st As String, Dat() As Byte
Dim Dlg As Object
Set Dlg = CreateObject("MSComDlg.CommonDialog")
With Dlg
.DialogTitle = "打开"
.Flags = &H200C
.CancelError = True
.Filter = "普通文本方式打开|*.txt|Unicode方式打开|*.txt|UTF-8方式打开|*.txt"
.ShowOpen
fName = .FileName
End With
ReDim Dat(FileLen(fName) - 1) As Byte
Open fName For Binary As #1
Get #1, , Dat
Close #1
Select Case Dlg.FilterIndex '根据用户选取的打开方式来进行相应的操作
Case 2 '如果是Unicode方式打开
st = Dat
If Dat(0) = &HFF And Dat(1) = &HFE Then st = Mid(st, 2) '如果有Unicode头
Case 3 '如果是UTF8方式打开
If Dat(0) = &HEF And Dat(1) = &HBB And Dat(2) = &HBF Then '如果有UTF8头
st = UTFtoGB(3, Dat)
Else '如果没有UTF8头
st = UTFtoGB(0, Dat)
End If
Case 1 '如果是普通文本方式打开
st = StrConv(Dat, vbUnicode)
End Select
Text1.Text = st
InErr:
End Sub
Private Function UTFtoGB(k As Long, Dat0() As Byte) As String '参数1-起始位置;参数2-UTF8数据
Dim L As Long, i As Long, j As Long, Dat1() As Byte
j = UBound(Dat0)
ReDim Dat1(j * 2)
For i = k To j
If Dat0(i) > 223 And Dat0(i) < 240 Then '如果是3字节UTF-8编码
Dat1(L + 1) = ((Dat0(i) And 15) * 16 + (Dat0(i + 1) And 60) / 4)
Dat1(L) = (Dat0(i + 1) And 3) * 64 + (Dat0(i + 2) And 63)
i = i + 2
Else '如果是Ascii字符或其它UTF-8编码
Dat1(L) = Dat0(i)
End If
L = L + 2
Next
ReDim Preserve Dat1(L - 1) '删除未用的数组元素
UTFtoGB = Dat1
End Function
Private Sub Command2_Click()
Dim Dat() As Byte
Dat = StrConv(Text1, vbFromUnicode)
Text1 = UTFtoGB(0, Dat)
End Sub
Private Sub Command3_Click()
On Error GoTo OutError
Dim Dat() As Byte, Dat1() As Byte, fName As String
Dim Dlg As Object
Set Dlg = CreateObject("MSComDlg.CommonDialog")
With Dlg
.Filter = "保存为普通文本文件|*.txt|保存为Unicode文件|*.txt|保存为UTF-8文件|*.txt"
.Flags = &H200A
.DialogTitle = "另存为"
.ShowSave
fName = .FileName
End With
Select Case Dlg.FilterIndex '根据用户选取的保存类型来进行相应的操作
Case 1 '保存为普通的TXT文件
Dat = StrConv(Text1.Text, vbFromUnicode)
Case 2 '保存为Unicode编码文件
ReDim Dat1(1) As Byte
Dat1(0) = &HFF: Dat1(1) = &HFE
Dat = Text1.Text
Case 3 '保存为UTF_8编码文件
Dim L As Long, k As Long, i As Long
Dat1 = Text1.Text: k = UBound(Dat1): ReDim Dat(k * 3)
For i = 0 To k - 1 Step 2
If Dat1(i + 1) = 0 Then '如果是Ascii字符
Dat(L) = Dat1(i): L = L + 1 '字节计数+1
Else
Dat(L) = (Dat1(i + 1) And 240) / 16 Or 224
Dat(L + 1) = (Dat1(i + 1) And 15) * 4 + ((Dat1(i) And 192) / 64) Or 128
Dat(L + 2) = Dat1(i) And 63 Or 128
L = L + 3
End If
Next
ReDim Preserve Dat(L - 1) As Byte '删除未用的数组元素
ReDim Dat1(2)
Dat1(0) = &HEF: Dat1(1) = &HBB: Dat1(2) = &HBF
End Select
Open fName For Binary As #1
If Dlg.FilterIndex > 1 Then Put #1, , Dat1
Put #1, , Dat
OutError:
Close
End Sub
简要说明:
在UTFtoGB函数中,我对2字节的UTF-8编码未作判断处理,因为不会遇到,如果你有需求,请自行设计代码。
点击按纽1是读入文本文件,包括普通的文本文件和Unicode编码文件以及UTF-8编码文件。
点击按纽2是将Text1的UTF-8字符转换为VB可以正常显示的字符,例如在分析一些网页代码时,经常会看到UTF8字符,你可以将其复制到Text1中,再点击按纽2进行转换。但会有个别字符变成了问号,这是因为当字符串复制到剪切板上时,剪切板把不能识别的编码统统作问号处理。
点击按纽3将Text1的文本保存为文件,你可以选择保存为普通的文本文件,也可以保存为Unicode编码文件或者UTF-8编码文件。
另外,你也许还会遇到一列包含“%”的16进制字符,这其实也是UTF-8数列,例如:
%E9%83%BD%E5%B8%82%E6%83%85%E7%B7%A3%E6%98%9F%E5%BA%A7
怎么办?不要紧,再在窗体上添加第4个按纽,编写代码对其进行转换。运行后,把这个数列复制到文本框,点击按纽4即可:
Private Sub Command4_Click()
Dim st As String, z() As String, Dat() As Byte, i As Integer, j As Integer
st = Replace(Text1, "%", "%&H")
z = Split(st, "%")
j = UBound(z)
ReDim Dat(j)
For i = 0 To j: Dat(i) = Val(z(i)): Next
Text1 = UTFtoGB(1, Dat)
End Sub