Căn bản Unicode cho VB6
programmers
ISO 10646 và
Unicode
ISO (International Standard Organisation)
10646 là tiêu chuẩn quốc tế nhằm cung ứng đủ số ký tự để dùng cho mọi
chữ của tất cả ngôn ngữ trên thế giới. Thay vì dùng 8 bits để biểu diễn
chỉ 255 dấu hiệu, bây giờ người ta dùng đến 16 bits để có thể biểu diễn
đến trên 65000 dấu hiệu.
Hãy tưởng tượng một bài viết có thể chứa
nhiều ngôn ngữ cùng một lúc. Hay môt chương trình hiển thị các đề mục bằng
ngôn ngữ địa phương, vì khi chương trình phát động nó nhận diện ra ngay là
nó đang đuợc chạy ở xứ nào bằng cách đọc Locale từ Windows
.
Một trong những ưu điểm của tiêu chuẩn nầy là sự cố gắng để xáp
nhập các tiêu chuẩn 8 bit có sẵn để không cần phải thay đổi chúng. Thật ra
ISO 10646 là tiêu chuẩn quốc tế chính thức, còn Unicode thì
được Unicode Consortium (tập hợp đại
diện các công ty Tin Học lớn) soạn ra. Nhưng cả hai tiêu chuẩn gần như y
hệt nhau khi nói đến con số dùng để biểu diễn một chữ, con số nầy đuợc gọi
là code point. Thí dụ như code point của chữ ả là 1EA3, của chữ ơ là 01A1. Ðể biểu diễn
một code point, tùy theo cách encoding, có khi ta cần 1, 2, 3 hay 4 bytes
.v.v., mỗi byte dùng cho code point người ta không gọi là byte hay octet,
mà lại gọi là code unit. Thỉnh thoảng, Unicode được cập nhật hóa,
và ấn bản mới nhất hiện giờ là 3.0.1.
ISO 10646 định nghĩa hai bộ
CCS (Coded Character Sets), UCS-2 và UCS-4. UCS-2
dùng 16 bits và là một phần nhỏ (subset) của UCS-4. UCS-4 là một CCS
dùng 31bits, chia thành 4 nhóm như sau:
| 7 bits |
8 bits |
8 bits |
8 bits |
| 1111111 |
11111111 |
11111111 |
11111111 |
| Group |
Plane |
Row |
Cell |
Cái Plane
đầu tiên của UCS-4 với giá trị Group=0, Plane=0 cũng là chính UCS-2. Nó
còn đuợc gọi là BMP (Basic Multilingual Plane).
Code points
trong UCS thường được viết dưới dạng u+????, mà ???? là con số
hexadecimal của code point. Characters có giá trị trong khoảng từ
u+0021 đến u+007E thì giống như ASCII và các
characters trong khoảng từ u+00A0 đến u+00FF thì giống như
ISO 8859-1. Do đó rất dễ cho ta hoán chuyển giữa ASCII hay ISO
8859-1 với UCS. Unicode (version 3.0.1) thì dùng 20bit subset của UCS-4
làm Coded Character Set.
Những Character Encoding
Schemes
Có vài Character Encoding Schemes
được đưa ra dùng: đó là UTF-8, UTF-16, UTF-16LE, và UTF-16BE.
UTF là viết tắt chữ Unicode Transformation
Format.
UTF-16
UTF-16 là một cách encoding dùng 20bit Unicode. Các characters
trong BMP được diễn tả bằng cách dùng giá trị 16bit của code point trong
Unicode CCS. Có hai cách để viết 16bit value trong một dòng (stream) 8bit
. Có lẽ bạn đã nghe qua chữ endian. Big Endian có nghĩa là
cho Most Significant Byte đi trước, tức là nằm bên trái - do đó ta có
UTF-16BE. Còn Little Endian thì ngược lại, tức là Least
Significant Byte đi trước - do đó ta có UTF-16LE. Thí dụ, giá trị
16bit của con số Hex1234 được viết là Hex12 Hex34 trong big
endian và Hex34 Hex12 trong little endian.
Những characters
không nằm trong BMP đuợc biểu diễn bằng cách dùng surrogate pair
(cặp thay thế). Code points có giá trị từ u+D800 đến u+DFFF được dành
riêng ra để dùng cho mục đích nầy. Trước hết, một code point có 20 bits
được phân ra làm hai nhóm 10 bits. Nhóm Most Significant 10 bits đuợc
mapped vào một giá trị 10bit nằm trong khoảng từ u+D800 đến u+DBFF. Nhóm
Least Significant 10 bits đuợc mapped vào một giá trị 10bit nằm trong
khoảng từ u+DC00 đến u+DFFF. Theo cách đó UTF-16 có thể biểu diễn được
những characters Unicode có 20bits.
UTF-8
UTF-8
là một cách encoding để có tác dụng y như UCS-4 (cũng là UTF-16), chớ
không phải có code point nào khác. UTF-8 được thiết kế cho
upward-compatible với ASCII. UTF-8 không phải là chỉ dùng một byte, nhưng
là dùng nhiều bytes - từ 1 đến 6 bytes.
Cách hoán chuyển UTF-16 qua
UTF-8 và ngược lại đuợc làm theo bảng dưới đây. Trong Table dưới đây,
những binary (Nhị phân) bits nằm bên trái của UTF-16 đuợc chuyển qua bên
phải của UTF-8 theo cùng một màu như chỉ dẩn. Ta hãy thử lấy thí dụ chữ ả với code point, hay UTF-16, 1EA3. UTF-8 bytes của nó là E1BAA3. Nếu bạn
chưa quen cách dùng số nhị phân hay Hex hãy đọc qua bài Hệ thống số nhị
phân.
UCS-4 (UTF-16)
1
E A
3 0001 1110 1010 0011 |
UTF-8
1110 ???? 10?? ???? 10??
???? 1110 0001 1011 1010 1010
0011 E
1 B
A A 3
|
UTF-16 (Nhị phân) |
UTF-8 (Nhị phân) |
| 00000000 00000000 00000000 0??????? |
0??????? |
| 00000000 00000000 00000???
???????? |
110????? 10?????? |
| 00000000 00000000 ???????? ???????? |
1110???? 10?????? 10?????? |
| 00000000 000????? ???????? ???????? |
11110??? 10?????? 10?????? 10?????? |
| 000000?? ???????? ???????? ???????? |
111110?? 10?????? 10?????? 10?????? 10?????? |
| 0??????? ???????? ???????? ???????? |
1111110? 10?????? 10??????
10?????? 10??????
10?????? |
Vì
chúng ta chỉ làm việc với một số tương đối ít Unicode code points ( tổng
cộng 134 ký tự) và UTF-8, bạn có thể hoặc dùng một Look-up table dựa theo
Bản
đối chiếu, hoặc dùng hai Functions ToUTF8 và ToUTF16 với
Listings như dưới đây để hoán chuyển từ encoding UTF-16 ra UTF-8, và ngược
lại:
Function ToUTF8(ByVal UTF16 As Long) As Byte() '
Convert a 16bit UTF-16BE to 2 or 3 UTF-8
bytes Dim bArray()
As Byte If
UTF16 < &H80 Then ReDim bArray(0) ' one byte
UTF-8 bArray(0) =
UTF16 ' Use number as
is ElseIf UTF16 <
&H800 Then ReDim bArray(1) ' two byte
UTF-8 bArray(1) = &H80
+ (UTF16 And &H3F) ' Least Significant 6
bits UTF16 = UTF16 \
&H40 ' Shift UTF16 number right 6
bits bArray(0) = &HC0 +
(UTF16 And &H1F) ' Use 5 remaining bits Else ReDim bArray(2) ' three
byte UTF-8 bArray(2) =
&H80 + (UTF16 And &H3F) ' Least Significant 6
bits UTF16 = UTF16 \
&H40 ' Shift UTF16 number right 6
bits bArray(1) = &H80 +
(UTF16 And &H3F) ' Use next 6
bits UTF16 = UTF16 \
&H40 ' Shift UTF16 number right 6 bits
again bArray(0) = &HE0
+ (UTF16 And &HF) ' Use 4 remaining bits End If
ToUTF8 = bArray ' Return UTF-8 bytes in an
array End Function
Function ToUTF16(bArray) As Long
' Convert 2 or 3 UTF-8 bytes to a 16bit
UTF-16BE Dim
IntUB IntUB = UBound(bArray) ' Find out how many bytes UTF-8
takes Select Case IntUB Case 0 ' one byte UTF-8.
Note that bArray starts with
index=0 ToUTF16 =
bArray(0) ' Use number as
is Case 1 ' two byte
UTF-8 ToUTF16 = (bArray(0)
And &H1F) * &H40 + (bArray(1) And &H3F) Case 2 ' three byte
UTF-8 ToUTF16 = (bArray(0)
And &HF) * &H1000 + (bArray(1) And &H3F) * &H40 + (bArray(2) And &H3F) End Select End Function
|
Dưới
đây là kết quả khi bạn click nút UTF-16 -->UTF-8 rồi click nút
UTF-8 -->UTF-16:

Bạn có thể download chương trình
Test hoán chuyển encodings giữa UTF-16 và UTF-8 để chạy
thử.
BOM (Byte Order Mark)
Vì Unicode nói chung hổ trợ UTF-16BE, UTF-16LE và UTF-8, nên
để có thể biết được Text file trong Microsoft Windows đang chứa Unicode
encoding kiểu nào, ở đầu mỗi Unicode Text file có 2 hay 3 bytes gọi là
Byte Order Mark như sau:
| Character Encoding Scheme |
Byte Order Mark |
| UTF-16BE |
FE FF |
| UTF-16LE |
FF FE |
| UTF-8 |
EF BB BF |
Khi viết Unicode ra Text file
trong MSWindows, nếu dùng UTF-8 bạn chỉ cần viết ở đầu file Byte Order
Mark EF BB BF. Các bytes kế tiếp cứ theo đúng hoặc UTF-8 characters
thì dùng 2 hay 3 bytes hoặc ANSI characters thì dùng 1 byte. Nếu dùng
UTF-16LE (còn gọi gọn là Unicode trong ngôn ngữ Microsoft) thì viết Byte
Order Mark FF FE. Kế đó mỗi character phải viết ra 2 bytes dù là
Unicode hay không. Nếu character chỉ cần 1 byte, kể cả các characters như
Carriage Return (&H0D) và LineFeed (&H0A), thì viết thêm byte thứ
nhì là 00 (Null byte). Thí dụ:
| Character Encoding Scheme |
Các bytes viết ra Text file |
| Text của file |
á à ả |
| UTF-16LE |
FF FE E1 00 20 00 E0 00 20 00 A3 1E |
| UTF-8 |
EF BB BF C3 A1 20 C3 A0 20 E1 BA A3 |
Khi
bạn đọc Text file vào từng byte một bằng VB6, nhớ mở File dưới dạng Binary
(as Binary) để có thể thấy luôn cả các Null bytes. Nếu bạn mở File
dưới dạng Text File (as Input) thì sẽ không thấy các Null bytes.
Câu hỏi đặt ra là Text File với encoding UTF-16LE hay UTF-8 chiếm nhiều
chỗ hơn. Câu trả lời là tùy theo trường hợp. Ðối với UTF-16 thì mỗi
character cần 2 bytes, kể cả ANSI characters. Ðối với UTF-8 thì nếu là
Unicode character thì cần 2 hay 3 bytes, còn ANSI character thì chỉ cần 1
byte.
Các Functions thông dụng cho
Unicode
Hoán chuyển ký tự từ Thường ra
Hoa
Chúng ta có tổng cộng 134 ký tự dùng cho
các nguyên âm có dấu và hai chữ đ, Ð. Nếu trong program có chứa danh sách
các Unicode với 67 chữ Thường theo sau bởi 67 chữ Hoa, ta có thể biến đổi
một chữ từ LowerCase ra UpperCase, hay ngược lại rất dễ dàng. Dưới đây
là Listing của Function UpperUniChar:
Function
UpperUniChar(Ch) As String ' Return
the Uppercase for a given vowel or dd Dim Pos ' Position of
character in Unicode vowel list ' Locate the character in list of Unicode
vowels Pos = InStr(UVowels,
Ch) If (Pos > 67) Then
UpperUniChar = Ch ' It's already uppercase -
leave it alone ElseIf
(Pos > 0) Then ' It's a Lowercase Unicode Vowel - so get the
corresponding Uppercase vowel in the
list UpperUniChar =
Mid(UVowels, Pos + 67, 1) Else ' It's just a normal ANSI
character UpperUniChar =
UCase(Ch) End If End Function
|
Dưới
đây là kết quả khi bạn click nút ToUppercase

Tương tự như vậy ta có Function LowerUniChar. Ðể
hoán chuyển cả một Unicode String ra chữ thường hay chữ Hoa ta có các
Function UpperUniStr và LowerUniStr.
Ngoài ra ta cũng có các
Functions IsUniChar, IsUpperUniChar để kiểm tra xem character có
phải là một Unicode Vowel hay là một Unicode Vowel Hoa.
Function
GetFileEncoding giúp ta nhận diện một Text file đã được encoded theo
UTF-16LE, UTF-8 hay ANSI.
Hoán chuyển ký tự cho trang
Web
Và sau cùng Function ToUniDecimal đổi Unicode
characters ra dạng ✏ thích hợp cho các trang Web. Listing
của Function ToUniDecimal như sau:
Function
ToUniDecimal(UniString As String) As String
' Return the HTML equivalent string of a Unicode
string Dim i As Integer ' Must declare as integer for CopyMemory to
work Dim TLen,
TStr Dim b1 As Byte
Dim b2 As Byte Dim
UTF16 As Long TLen = Len(UniString)
' Get Length of input Unicode
string If TLen = 0
Then Exit Function ' Get out if null
string ' Iterate through
each character in the string For i = 1 To
TLen If
IsUniChar(Mid(UniString, i, 1)) Then
' Cast the String character to 2
bytes
CopyMemory b1, ByVal StrPtr(UniString) + ((i
- 1) * 2), 1
CopyMemory b2, ByVal StrPtr(UniString) + ((i
- 1) * 2) + 1, 1
' Combine the 2 bytes into the Unicode
UTF-16
UTF16 = b2 ' assign b2 to UTF16 before
multiplying by 256 to avoid
overflow
UTF16 = UTF16 * 256 +
b1 ' Convert UTF-16 to format ✏ for
HTML TStr
= TStr & "&#" & Trim(CStr(UTF16)) &
";" Else
' Get here if it;s an ANSI
character
TStr = TStr & Mid(UniString, i,
1) End
If Next ToUniDecimal = TStr
' Return the HTML string End Function |
Dưới đây là
kết quả khi bạn click nút ToUniDecimal
Bạn có thể download chương
trình Test có tất cả các Functions để dùng cho Unicode.
Ðọc viết Text file dưới dạng Unicode và
UTF-8
Program Microsoft Notepad trong WindowsNT cho ta đánh
Text dưới dạng ANSI, Unicode hay UTF-8. Ta có thể lập trình VB6 để đọc và
viết Text file dưới cả hai dạng của Unicode. Khi Microsoft dùng chữ Save
As Unicode là nói đến UTF-16LE.
Cách gọn và tiện nhất để đọc và
viết Unicode (UTF-16LE) là dùng FileSystemObject của Microsoft
Script Runtime. Khi Fs.OpenTextFile nhớ dùng TristateTrue để cho
biết bạn muốn đọc viết Unicode Text.
Public
Function ReadTextFile(FileName) As String '
Write a Unicode String to UTF-16LE Text file
' Remember to Project | References "Microsoft
Scripting Runtime" to support ' FileSystemObject &
TextStream Dim Fs
As FileSystemObject
Dim TS As
TextStream ' Create a
FileSystem Object Set
Fs =
CreateObject("Scripting.FileSystemObject")
' Open TextStream for
Input. ' TriStateTrue means
Read Unicode UTF-16LE Set TS = Fs.OpenTextFile(FileName, ForReading,
False, TristateTrue) ReadTextFile =
TS.ReadAll ' Read the whole content of the
text file in one stroke TS.Close ' Close the Text Stream Set Fs = Nothing
' Dispose FileSystem Object End Function Public Sub
WriteTextFile(FileName, StrOutText) ' Read a Unicode String from UTF-16LE Text
file ' Remember to Project
| References "Microsoft Scripting Runtime" to
support ' FileSystemObject
& TextStream Dim
Fs As FileSystemObject
Dim TS As
TextStream ' Create a
FileSystem Object Set
Fs =
CreateObject("Scripting.FileSystemObject")
' Open TextStream for Output, create file if
necesssary ' TriStateTrue
means Write Unicode UTF-16LE Set TS = Fs.OpenTextFile(FileName, ForWriting,
True, TristateTrue) TS.Write StrOutText
' Write the whole StrOutText string in one
stroke TS.Close '
Close the Text Stream Set Fs = Nothing
' Dispose FileSystem Object End Sub
|
Cách
gọn và tiện nhất để đọc và viết UTF-8 text files là dùng một VB6 Class tên
clsUnicodeText dựa vào MS DOM (Document Object Model) và XML như
sau:
Dim MyUnicodeText As clsUnicodeText Set MyUnicodeText = New clsUnicodeText '
Read Unicode Text from file txtFileName and display in
TextBox1 TextBox1.Text =
MyUnicodeText.ReadUnicode(txtFileName) |
Listing
của Class clsUnicodeText như sau:
Option
Explicit Private mDOMTextFile As
DOMDocument Private mXMLPath As String Public Function
ReadUnicode(TXMLPath) Dim objTextFileRoot As
IXMLDOMElement Set
mDOMTextFile = New
DOMDocument mXMLPath =
TXMLPath mDOMTextFile.Load
mXMLPath 'start at the root
element of the XML Set objTextFileRoot =
mDOMTextFile.documentElement ReadUnicode =
objTextFileRoot.nodeTypedValue End
Function
Public Sub
WriteUnicode(OutText, Optional
TXMLPath) Dim tDOMNode
As IXMLDOMElement '
Temporary Node for DOM If IsMissing(TXMLPath) Then ' Save the information on the screen by creating a new
element and add its children to the DOM
object
mDOMTextFile.documentElement.Text =
OutText '
Update the XML file
mDOMTextFile.save mXMLPath Else Set mDOMTextFile = New
DOMDocument ' Create a Node called "Text" in
DOM Set tDOMNode =
mDOMTextFile.createElement("Text")
' Make it the Root
Node
mDOMTextFile.appendChild
tDOMNode '
Assign Output Text to Root
Node
mDOMTextFile.documentElement.Text =
OutText '
Update the XML file
mDOMTextFile.save TXMLPath End If End Sub
|
Nhưng
nếu bạn muốn viết và đọc trực tiếp một Text file với encoding UTF-16LE hay
UTF-8 thì bạn cũng có thể làm được với VB6 bằng cách đọc và viết trong
Binary mode, tức là Open file as Binary. Khi viết ra thì sau khi viết
BOM bytes, ta viết hoặc 2 bytes cho mỗi UTF-16 character trong UTF-16LE
encoding, hoặc 2 hay 3 bytes cho Unicode character và 1 byte cho ANSI
trong UTF-8 encoding.
Khi đọc vào, đối với UTF-16LE thì ta dùng
trực tiếp 2 bytes cho mỗi character, nhưng đối với UTF-8 thì ta phải nhận
ra &HE1 là byte đi đầu của 3 byte character , &HC3, &HC4,
&HC5 và &HC6 là byte đi đầu của 2 byte character, để có thể hoán
chuyển ra Unicode character cho input string. Các kỹ thuật nầy được cắt
nghĩa trong Listing của Function UTF8ToUniStr như dưới đây:
Function
UTF8ToUniStr(BArray) As String '
Convert a byte stream of UTF-8 to Unicode
String Dim i As Long Dim TopIndex As Long Dim TwoBytes(1) As Byte Dim ThreeBytes(2) As Byte Dim AByte As Byte Dim TStr As String TopIndex =
UBound(BArray) ' Number of bytes equal
TopIndex+1 If
TopIndex = 0 Then Exit Function ' get out if there's nothing to
convert i = 0 '
Initialise pointer '
Iterate through the Byte Array Do While i <=
TopIndex AByte =
BArray(i) ' fetch a
byte If AByte = &HE1 Then
' Start of 3 byte UTF-8 group for a
character
' Copy 3 byte to
ThreeBytes
ThreeBytes(0) = BArray(i): i = i +
1
ThreeBytes(1) = BArray(i): i = i +
1
ThreeBytes(2) = BArray(i): i = i +
1 ' Convert Byte array to UTF-16 then
Unicode
TStr = TStr &
ChrW(ToUTF16(ThreeBytes))
ElseIf (AByte >= &HC3) And (AByte <= &HC6) Then
' Start of 2 byte UTF-8 group for a
character
TwoBytes(0) = BArray(i): i = i +
1
TwoBytes(1) = BArray(i): i = i +
1 ' Convert Byte array to UTF-16 then
Unicode
TStr = TStr &
ChrW(ToUTF16(TwoBytes))
Else
' Normal ANSI character - use it as
is TStr =
TStr & Chr(AByte): i = i + 1 ' Increment
byte array index End If Loop UTF8ToUniStr =
TStr ' Return the resultant
string End Function |
Bạn có thể download program
UniTextInOut.zip với tất cả các Functions cần thiết. Program nầy
biểu diễn các cách khác nhau để đọc và viết Text files với encodings
UTF-16LE và UTF-8. Click nút Browse để chọn Text file (file
TestUTF8.txt có UTF-8 encoding, file TestUTF16.txt có UTF-16LE encoding),
rồi click nút DisplayFileHex để hiển thị bài chữ Việt bên trái và
Hex string của file bên phải. Ðồng thời các Radio button cho biết là Text
file có encoding Unicode (i.e. UTF-16LE) hay UTF-8. Kế đó bạn có thể chọn
loại encoding bằng cách click lên Radiobutton Unicode hay UTF-8 trước khi
click nút Save để lưu trử data trong Textbox bên trái vào Text file
theo encoding bạn đã chọn.
Click vào đây để xem Listings của 13
Functions dùng để xử lý Unicode chữ Việt.
Những References và Components hổ trợ
Unicode trong VB6
Trước hết muốn hiển thị Unicode cho chữ Việt
ta cần phải dùng Menu command của VB6 IDE để Project | Components
Microsoft Forms 2.0 Object Library. Cái ActiveX nầy cho ta những
Label, TextBox, Listbox và ComboBox cần thiết để hiển thị chữ Việt trong
Unicode.
Kế đó, để tiện việc đọc và viết chữ Việt dưới dạng
UTF-8, ta chứa Unicode text file trong một XML file giữa một
cặp tags tên Text hay tên gì cũng được (đó cũng là root node) , rồi
dùng Microsoft Document Object Model (DOM) để đọc và viết chữ Việt.
Bạn nhớ Project | References Microsoft XML, v3.0. Nguyên phần Text
là nodeTypedValue của root node của DOM.
Làm như thế ta
khỏi phải đọc từng byte rồi tìm cách chuyển data ấy qua Unicode
String. Ở đây xin nhấn mạnh là bạn phải vui lòng dùng MSWindowsNT hay
MSWindows2000 mới được. Bạn có thể dùng Notepad trong WindowsNTđể edit XML
file chứa chữ Việt và lưu trử dưới format UTF-8.
Ngoài ra để
hiển thị record data chữ Việt Unicode trong một Grid bạn cũng cần phải
Project | Components Microsoft Hierarchical FlexGrid Control 6.0
(OLEDB).
Một khi Unicode text đã đuợc đọc vào trong VB6 Text String rồi, nó đuợc
dùng y hệt như cho ANSI characters. Ðó là vì bên trong VB6 mỗi Unicode
character hay ANSI character đều chiếm 2 bytes. Nói như thế có nghĩa
là các Functions Left, Mid, InStr đều có thể đuợc dùng cho Unicode
Text String như một ANSI String bình thường.
Xin lưu ý: Hình chữ Ð có hai code points: &HD0 và
&H110. Code point &HD0 là của chữ Latin Eth hoa ( chữ Latin
Eth thường là
ð)
, còn code point &H110 là của chữ Latin d hoa với stroke ( chữ Latin d thường với stroke là
đ
)
. Do đó ta phải dùng code point &H110. Bạn
có thể copy chữ Ð nầy ngay từ Browser để paste vào chỗ nào cần thiết.
|