• Thứ Ba, 09/05/2006 15:57 (GMT+7)

    Góp ý bài viết sắp thứ tự chữ Việt Unicode

    Tôi có đọc được hai bài viết của tác giả Phạm Văn Trung về kĩ thuật sắp xếp tiếng Việt Unicode đăng trên tạp chí TGVT (tháng 12/2004 - tr.140 và tháng 2/2005 - tr.115). Tôi áp dụng với cơ sở dữ liệu thử nghiệm - khoảng 60 bản ghi với họ và tên tương đối đơn giản thì chương trình làm việc chính xác. Tuy nhiên, khi áp dụng cho cơ sở dữ liệu thật - khoảng 400 bản ghi với nhiều tên họ phức tạp thì độ chính xác của việc sắp xếp (theo đúng qui tắc mà tác giả đã nêu ở bài viết thứ hai) chỉ đạt khoảng 85%, khá nhiều bản ghi xếp sai vị trí.

    Dựa trên những ý tưởng về thuật toán mà tác giả Phạm Văn Trung đưa ra, tôi đã viết một chương trình khác để giải quyết triệt để tất cả các trường hợp xuất hiện của tên họ tiếng Việt. Thuật toán có thể tóm tắt như sau:

    - Tách chuỗi kí tự của trường Họ và Tên thành những từ đơn, kể cả trường hợp Họ và Tên nằm trong một trường hay hai trường khác nhau.

    - Chuyển những kí tự của từ đơn thu được ở trên thành dạng kí tự thuần La tinh:

    + Nếu là kí tự thuần La tinh thì giữ nguyên (a, d, e, o, u...)

    + Nếu là kí tự tiếng Việt (á, ă, â, đ, ớ...) thì chuyển thành dạng kí tự thuần La tinh nhưng thêm một hoặc hai kí tự "z" vào sau kí tự đó (ă = az, â = azz...)

    + Nếu là kí tự tiếng Việt không có dấu thanh thì thêm "1" vào cuối từ.

    + Nếu là kí tự tiếng Việt có dấu thanh thì thêm các số từ "2" đến "6" vào cuối từ, tương ứng với các dấu huyền, hỏi, ngã, sắc, nặng.

    - Qua những thao tác này, toàn bộ phần Họ và Tên sẽ được mã hóa thành dạng kí tự thuần La tinh. Việc mã hóa này tạo điều kiện để Access có thể sắp xếp cơ sở dữ liệu bằng lệnh Sort bình thường, mà vẫn thực hiện đúng qui luật sắp xếp tiếng Việt là sắp xếp theo kí tự trước rồi mới sắp xếp theo dấu thanh.

    Các bước tiến hành cụ thể như sau:

    Bước 1:

    Tạo ra một form trên đó có chứa một số chuỗi kí tự mẫu tiếng Việt (do VBA chưa hỗ trợ gõ trực tiếp tiếng Việt trong cửa sổ mã lệnh) dùng làm các chuỗi kí tự tham khảo cho các hàm xử lí tiếng Việt. Ý tưởng này tôi đã "copy" từ tác giả Phạm Văn Trung.

    - Vào Excel | Tools | Macro | Visual Basic Editor, chọn Insert | UserForm. Kích phải form, chọn Properties. Đặt tên form là Bieumau.

    - Chọn View | Toolbox, chọn công cụ TextBox, chèn bốn textbox vào form, đặt tên lần lượt là TextKhongdau, TextCodau, TextAnhxa, và TextTiengViet.

    - Nhập hoặc sao chép những chuỗi kí tự sau vào textbox tương ứng:

    TextKhongdau:

    "AaĂăÂâBbCcDdĐđEeÊêFfGgHhIiJjKkLlMmNnOoÔôƠơPpQqRrSsTtUuƯưVvWwXxYyZz"

    TextCodau:

    "ÀàẢảÃãÁáẠạẰằẲẳẴẵẮắẶặẦầẨẩẪẫẤấẬậÈèẺẻẼẽÉéẸẹỀềỂểỄễẾếỆệÌìỈỉĨĩÍíỊịÒòỎỏÕõÓóỌọỒồỔổỖỗỐốỘộỜờỞởỠỡỚớỢợÙùỦủŨũÚúỤụỪừỬửỮữỨứỰựỲỶỸÝỴỳỷỹýỵ”

    TextAnhxa:

    "aaaaaaaaaaăăăăăăăăăăââââââââââeeeeeeeeeeêêêêêêêêêêiiiiiiiiiiooooooooooôôôôôôôôôôơơơơơơơơơơuuuuuuuuuuưưưưưưưưưưyyyyyyyyyy"

    TextTiengViet:

    "ăâđêôơư”

    - Chọn File | Export File, đặt tên form là Bieumau, chọn thư mục để lưu form, rồi nhấn vào nút Save.

    - Đóng Excel và không lưu gì cả.

    Bước 2:

    - Vào Access, mở cơ sở dữ liệu liên quan.

    - Chọn Tools | Macro | Visual Basic Editor

    - Chọn File | Import File, chọn tập tin Bieumau.frm đã lưu ở bước 1. Trong cửa sổ Project Explorer sẽ xuất hiện thư mục Forms, trong đó có đối tượng Bieumau. Nếu không thấy cửa sổ Project Explorer, chọn View | Project Explorer.

    - Chọn Insert | Module để chèn một mô đun mã lệnh mới.

    - Nhập hoặc sao chép toàn bộ phần mã lệnh sau vào cửa sổ soạn thảo:

    Option Compare Database

    Option Explicit

    Option Base 1

    Private Function Anhxa(Chuoikitu As String) As String

    'Chuyen tat ca cac ki tu thanh dang ki tu thuan Latinh

    Dim Chuoicodau As String, Chuoianhxa As String, Vitrikitu As Integer

    Dim i As Integer, Dodaichuoikitu As Integer, Kitu As String, Ketqua As String

    Dim ChuoiTiengViet As String

    ChuoiTiengViet = Trim(Bieumau.TextTiengViet)

    Chuoicodau = Trim(Bieumau.TextCodau)

    Chuoianhxa = Trim(Bieumau.TextAnhxa)

    Dodaichuoikitu = Len(Chuoikitu)

    Ketqua = ""

    Anhxa = ""

    For i = 1 To Dodaichuoikitu - 1

    Kitu = Mid(Chuoikitu, i, 1)

    Vitrikitu = InStr(Chuoicodau, Kitu)

    If Vitrikitu = 0 Then

    Ketqua = Ketqua + LCase(Kitu)

    Else

    Ketqua = Ketqua + Mid(Chuoianhxa, Vitrikitu, 1)

    End If

    Next i

    For i = 1 To Dodaichuoikitu - 1

    Kitu = Mid(Ketqua, i, 1)

    Vitrikitu = InStr(ChuoiTiengViet, Kitu)

    Select Case Vitrikitu

    Case 1

    Kitu = "az"

    Case 2

    Kitu = "azz"

    Case 3

    Kitu = "dz"

    Case 4

    Kitu = "ez"

    Case 5

    Kitu = "oz"

    Case 6

    Kitu = "ozz"

    Case 7

    Kitu = "uz"

    End Select

    Anhxa = Anhxa + Kitu

    Next i

    End Function

    Private Function Chuyenmadau(Chuoikitu As String) As String

    'Chuyen doi dau cua ki tu tieng Viet thanh cac con so

    Dim Vitrikitu As Integer, Sodu As Integer, Chuoicodau As String, Kitu As String

    Chuoicodau = Bieumau.TextCodau

    Kitu = Right(Chuoikitu, 1)

    Vitrikitu = InStr(Chuoicodau, Kitu)

    If Vitrikitu = 0 Then

    Chuyenmadau = "1"

    Else

    Sodu = Vitrikitu Mod 10

    Select Case Sodu

    Case 1, 2

    Chuyenmadau = "2"

    Case 3, 4

    Chuyenmadau = "3"

    Case 5, 6

    Chuyenmadau = "4"

    Case 7, 8

    Chuyenmadau = "5"

    Case 9, 0

    Chuyenmadau = "6"

    End Select

    End If

    End Function

    Private Function Mahoatu(Chuoikitu As String) As String

    'Chuyen 1 tu don thanh dang 1 cum ki tu thuan Latinh

    Dim i As Integer, Kitu As String

    Dim Dodaichuoikitu As Integer, Vitrikitu As Integer

    Dim Chuoikiemtra As String, Ketqua As String

    Chuoikiemtra = Trim(Bieumau.TextCodau) + Trim(Bieumau.TextKhongdau)

    Dodaichuoikitu = Len(Chuoikitu)

    For i = 1 To Dodaichuoikitu

    Kitu = Mid(Chuoikitu, i, 1)

    Vitrikitu = InStr(Chuoikiemtra, Kitu)

    If Vitrikitu <= 120 Then

    Ketqua = Chuoikitu + Kitu

    Exit For

    Else

    Ketqua = Chuoikitu + "a"

    End If

    Next i

    Mahoatu = Anhxa(Ketqua) + Chuyenmadau(Ketqua)

    End Function

    Public Function Chuyendoi(Chuoikitu As String, Optional Kieu As Integer = 1)

    'Cat ho ten thanh tung cum tu don roi sap xep

    Dim Ketqua As String, Kitu As String

    Dim x As Integer, y As Integer, z As Integer, Dodaichuoi As Integer

    Dim Mangtu() As String

    Chuoikitu = Chuoikitu + " "

    x = 1 'Day la bien dem va tro ve gia tri cu sau moi vong lap

    y = 1 'Day la bien dem de tang phan tu cho mang Mangtu() sau moi vong lap

    Ketqua = ""

    Do While Chuoikitu <> ""

    Dodaichuoi = Len(Chuoikitu)

    Kitu = Mid(Chuoikitu, x, 1)

    If Kitu = " " Then

    ReDim Preserve Mangtu(y)

    Mangtu(y) = Left(Chuoikitu, x - 1)

    Chuoikitu = Right(Chuoikitu, Dodaichuoi - x)

    x = 0

    y = y + 1

    End If

    x = x + 1

    Loop

    Select Case Kieu

    Case 1 'Ho va ten nam o hai truong khac nhau (mac dinh)

    For z = 1 To y - 1 Lay tung phan tu cua Mangtu() de xu li

    Ketqua = Ketqua + " " + Mahoatu(Mangtu(z))

    Next z

    Ketqua = Trim(Ketqua)

    Case 2 'Ho va ten nam o cung mot truong

    For z = 1 To y - 2

    Ketqua = Ketqua + " " + Mahoatu(Mangtu(z))

    Next z

    Ketqua = Mahoatu(Mangtu(y - 1)) + " " + Trim(Ketqua) 'Lay phan Ten dat len truoc

    End Select

    Chuyendoi = Ketqua

    End Function

    - Chọn File | Save, trả lời Yes đối với tất cả các câu hỏi của chương trình.

    - Đóng cửa sổ Visual Basic Editor.

    Lưu ý về cách sử dụng hàm Chuyendoi: Hàm số được thiết kế để có thể sử dụng cho cả hai trường hợp Họ và Tên nằm ở hai trường khác nhau, hoặc Họ và Tên nằm trong cùng một trường. Lệnh Chuyendoi(Chuoikitu, 1) hoặc Chuyendoi(Chuoikitu) (số 1 là mặc định nên có thể không cần nhập vào) sẽ mã hóa chuỗi kí tự liên quan theo trường hợp 1 nói trên, và lệnh Chuyendoi(Chuoikitu, 2) sẽ mã hóa chuỗi kí tự theo trường hợp 2. Nếu là trường hợp 2, bạn cần nhập lệnh Chuyendoi cho cả hai trường, trường Tên trước, rồi đến trường Họ.

    Bước 3:

    - Tạo một truy vấn mới bằng chế độ Design. Chọn bảng liên quan và nhập tất cả các trường cần thiết.

    - Nếu Họ và Tên nằm trong cùng một trường, nhập hàm chuyendoi([Tên bảng hoặc truy vấn liên quan]![Trường Họ Tên], 2) vào một cột trống. Chọn kiểu sắp xếp là Ascending hay Descending tùy theo yêu cầu. (xem hình)



    - Nếu Họ và Tên nằm trong hai trường khác nhau, nhập chuyendoi(Tên bảng hoặc querry liên quan]![Trường Tên]) vào một cột trống, nhập chuyendoi(Tên bảng hoặc querry liên quan]![Trường Họ]) vào cột trống bên cạnh. Chọn kiểu sắp xếp là Ascending hay Descending tùy theo yêu cầu cho cả hai cột.

    - Chạy truy vấn, bạn sẽ có kết quả sắp xếp theo đúng qui luật sắp xếp tiếng Việt.

    Tôi đã chạy thử đoạn mã lệnh của mình cho 5 cơ sở dữ liệu khác nhau, với số bản ghi từ 300 - 600, ba trong số đó có Họ và Tên nằm trong cùng 1 trường, hai cơ sở dữ liệu còn lại có Họ và Tên nằm trong hai trường khác nhau, và kết quả thu được đều hoàn toàn chính xác. Tuy nhiên, do kiến thức về lập trình còn hết sức sơ đẳng, nên tôi rất mong quí vị độc giả đóng góp ý kiến để chương trình có thể hoàn thiện hơn.

    Nguyễn Bảo Ngọc
    Email: thisisngoc@yahoo.com

    ID: A0604_138