• Vui lòng đọc nội qui diễn đàn để tránh bị xóa bài viết
  • Tìm kiếm trước khi đặt câu hỏi

Từng bước xây dựng bộ gõ Tiếng Việt

Các bài viết hướng dẫn, giúp các bạn hiểu và tiếp cận với Visual Basic nhanh hơn
Hình đại diện của người dùng
tuyen_dt18
Guru
Guru
Bài viết: 256
Ngày tham gia: T.Bảy 19/04/2008 8:46 pm
Đến từ: Hưng Yên
Been thanked: 43 time
Liên hệ:

Từng bước xây dựng bộ gõ Tiếng Việt

Gửi bàigửi bởi tuyen_dt18 » T.Hai 08/07/2013 8:50 pm

Tên bài viết: Từng bước xây dựng bộ gõ Tiếng Việt
Tác giả: tuyen_dt18
Cấp độ bài viết: Chưa đánh giá
Tóm tắt: Bài viết nhằm giúp cho các bạn có cái nhìn cơ bản về cách thức xây dựng một chương trình xử lý việc nhập Tiếng Việt trên Windows bằng ngôn ngữ lập trình VB6. Thảo luận thêm về chủ đề tại đây.



A. GIỚI THIỆU
1. Về bài viết
Sau một thời gian gián đoạn, thời gian này mình có rảnh một chút thời gian, đồng thời cũng do mình ấp ủ lâu rồi nên hôm nay muốn ngồi viết bài này hy vọng nó có ích cho các bạn.
Do trình văn của mình không được tốt lắm nên trong quá trình đọc các bạn chịu khó nhé. Mình sẽ cố gắng giải thích một cách kỹ nhất đề các bạn có thể hiểu được.

Trong bài viết này, mong các bạn không thảo luận, mình sẽ lập một bài khác để thảo luận trực tiếp về bài viết cũng như kiến thức khác liên qua đến kỹ thuật xây dưngj bộ gõ

Truy cập tại đây : viewtopic.php?f=22&t=27081

2. Về bộ gõ Tiếng Việt
Như chúng ta đã từng biết hay đã từng sử dụng, bộ gõ Tiếng Việt là một chương trình có mục đích giúp chúng ta nhập được những chữ cái mà bàn phím máy tính của chúng ta không hỗ trợ nhập trực tiếp (đây chỉ là nhứng chức năng cơ bản). Sau đây tôi xin được trình bày những hiểu biết của mình về nguyên lý của một chương trình như vậy, về phương diện là một nhà phát triển.
Một chương trình xử lý bàn phím (sau đây được gọi là bộ gõ) thực hiện những việc cơ bản sau :
- Bắt sự kiện gõ phím để lấy dữ liệu về bàn phím : Trong kỹ thuật lập trình trên Windows gọi là Hook bàn phím.
- Xử lý dữ liệu bàn phím : Là quá trình tạo ra các ký tự mong muốn từ nhưng ký tự đơn lẻ nhập vào từ bàn phím. Ví dụ : nhập "caau lacj booj VB6" -> xử lý thành "câu lạc bộ VB6".
- Kết xuất kết quả : Sau khi xử lý và tạo ra kết quả mong muốn, cần kết xuất kết quả đó. Cụ thể là những cửa sổ nhập văn bản của các ứng dụng đang được người dùng kích hoạt (activate).


B. TỪNG BƯỚC XÂY DỰNG
I. Bắt sự kiện gõ phím để lấy dữ liệu về bàn phím
1. Kỹ thuật Hook bàn phím

Các bạn có thể tham khảo các tài liệu và bài viết liên quan :
KeyboardProc callback function

  1. Private Declare Function GetKeyboardState Lib "user32" (pbKeyState As KeyboardState) As Long
  2. Private Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal nCode As Long, ByVal wParam As Long, lParam As Any) As Long
  3. Private Declare Function ToAscii Lib "user32" (ByVal uVirtKey As Long, ByVal uScanCode As Long, lpbKeyState As KeyboardState, lpwTransKey As Long, ByVal fuState As Long) As Long
  4.  
  5. Private Const HC_ACTION = 0
  6. Private Const VK_CONTROL = &H11
  7. Private Const VK_BACK = &H8
  8. Private Const VK_INSERT = &H2D
  9. Private Const VK_SHIFT = &H10
  10. Private Const VK_LSHIFT = &HA0
  11. Private Const VK_RSHIFT = &HA1
  12.  
  13. Private Const KeyIsReleasedFlag = &H80000000
  14. Private Const KeyScanCodeFlag = &HFF0000
  15.  
  16. Private Type KeyboardState
  17.     Key(0 To 255) As Byte
  18. End Type
  19.  
  20. Private m_hKeyboardHook             As Long
  21. Private m_KeyState                  As KeyboardState
  22. Private m_IsMyBack                  As Boolean
  23. Private m_BackCounts                As Long
  24. Private m_KeyCounts                 As Long
  25. Private m_IsMyPaste                 As Boolean
  26.  
  27. Private Function HookKeyboardProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  28.     '
  29.    If nCode = HC_ACTION Then
  30.         Dim lpRet As Long, lpChar As Long
  31.        
  32.         If (lParam And KeyIsReleasedFlag) = 0 Then
  33.             '
  34.            lpRet = GetKeyboardState(m_KeyState)
  35.             If (m_KeyState.Key(VK_CONTROL) And &H80) Then
  36.                 'Call ClearBuffer()
  37.                HookKeyboardProc = CallNextHookEx(m_hKeyboardHook, nCode, wParam, lParam)
  38.                 Exit Function
  39.             End If
  40.             lpRet = ToAscii(wParam, lParam And KeyScanCodeFlag, m_KeyState, lpChar, ByVal 0)
  41.             If wParam = VK_BACK Then
  42.                 If m_IsMyBack Then
  43.                     HookKeyboardProc = CallNextHookEx(m_hKeyboardHook, nCode, wParam, lParam)
  44.                     '
  45.                    m_IsMyBack = False
  46.                     Exit Function
  47.                 End If
  48.             End If
  49.             If lpRet = 1 Then
  50.                 'Call ProcessChar(lpChar)
  51.                If (m_BackCounts > 0) And (m_KeyCounts > 0) Then
  52.                     'Call PushBackChar()
  53.                    HookKeyboardProc = True
  54.                     Exit Function
  55.                 End If
  56.                 HookKeyboardProc = CallNextHookEx(m_hKeyboardHook, nCode, wParam, lParam)
  57.             Else
  58.                 If ((wParam = VK_INSERT)) Then
  59.                     If (m_KeyState.Key(VK_SHIFT) And &H80) Then
  60.                         If Not m_IsMyPaste Then
  61.                             'Call ClearBuffer()
  62.                        Else
  63.                             m_IsMyPaste = False
  64.                         End If
  65.                     End If
  66.                 ElseIf (wParam <> VK_LSHIFT) And (wParam <> VK_RSHIFT) And (wParam <> VK_SHIFT) Then
  67.                     'Call ClearBuffer()
  68.                End If
  69.                 '
  70.                HookKeyboardProc = CallNextHookEx(m_hKeyboardHook, nCode, wParam, lParam)
  71.             End If
  72.         End If
  73.        
  74.         HookKeyboardProc = CallNextHookEx(m_hKeyboardHook, nCode, wParam, lParam)
  75.     Else
  76.         HookKeyboardProc = CallNextHookEx(m_hKeyboardHook, nCode, wParam, lParam)
  77.     End If
  78. End Function


GetKeyboardState: Lấy về hiện trạng của tất cả các phím trên bàn phím
CallNextHookEx :Chuyển giao quyền xử lý phím cho chương trình xử lý trước đó hoặc mặc định hệ thống (khi không cần tác động thay đổi dữ liệu bàn phím).
ToAscii :Trong thủ tục hook ta chỉ bắt được mã ký tự không phân biệt dạng viết HOA -thường. Vì vậy hàm này sẽ dựa vào ngữ cảnh của bàn phím đề chuyển về dạng đúng.
m_KeyState :Biến lưu trạng thái bàn phím
m_IsMyBack :Biến đánh dấu rằng phím BACK được nhấn là do người dùng hay do chương trình của chúng ta tạo ra.
m_BackCounts :Xác định số lần cần nhấn phím BACK để xóa các ký tự hiện tại trên của sổ nhập văn bản của ứng dụng (để sau đó thay bằng các ký tự mới)
m_KeyCounts : Độ dài chuối bộ đệm ký tự
m_IsMyPaste :Đánh dấu tổ hợp phím SHIFT+INSERT là do người dùng nhấn hay do chương trình của chúng ta tạo ra để xuất ký tự ra cửa sổ nhập văn bản của ứng dụng.
HookKeyboardProc :
Trong hàm xử lý này, ta chỉ quan tâm khi phím được nhấn mà không quan tâm khi phím được thả ra (release) tức là khi nCode = HC_ACTION và (lParam And KeyIsReleasedFlag) = 0

Trước khi xử lý, ta lưu trạng thái hiện tại của bàn phím : GetKeyboardState(m_KeyState)
Sau đó, kiểm tra xem phím Ctrl có đang được nhấn hay không. Nếu được nhấn thì ta xóa bộ đệm ký tự.
Tiếp theo, ta lấy ra ký tự từ dữ liệu bàn phím bắt được bằng hàm ToAscii
Ta kiểm tra khi một phím BACK được nhấn để xác định xem phím BACK do chương trình của chúng ta tạo ra hay do người dùng nhấn dựa vào biến m_IsMyBack.
Tiếp đến, ta kiểm tra xem phím được nhấn là phím ký tự hay phím chức năng, phím mở rộng dựa vào kết quả trả về của hàm ToAscii, từ đó xử lý tiếp.
Nếu là ký tự thường, ta sẽ chuyển cho một hàm khác để xử lý. Nên viết riêng một hàm xử lý để code nhìn gọn và dễ debug.
Trong trường hợp không phải ký tự bình thường, ta lại kiểm tra tiếp xem tổ hợp SHIFT+INSERT có được nhấn không. Nếu có, ta kiểm tra xem tổ hợp này do chương trình của chúng ta tạo ra để xuất bộ đệm hay do người dùng nhấn bằng cách dựa vào biến m_IsMyPaste. Biến này được Set = TRUE trong hàm giả lập tổ hợp SHIFT+INSERT của chúng ta, và sau khi bắt gặp tổ hợp này và kiểm tra xong, ta phải Reset về False. Nếu không phải do chúng ta tạo ra, gọi àm xóa bộ đệm tại đây. Với các phím khác, ta luôn xóa bộ đệm và không tác động gì lên dữ liệu bàn phím.

2. Mã nguồn Hook bàn phím
Tải về :
HookKeyboard.zip
HokKeyboard
(2.07 KiB) Đã tải 259 lần



II. Xử lý dữ liệu bàn phím

1. Phương thức xử lý
a. Xử lý chung
Đầu tiên các bạn tạm thời tạo một hàm như sau :
  1. Private Sub ProcessChar(ByVal lpChar As Long)
  2. End Sub

Sau đó đặt việc gọi hàm này vào vị trí tương ứng trong hàm HookKeyboardProc (Call ProcessChar(lpChar)).
Hàm này nhập vào ký tự từ hàm Hook bàn phím để xử lý, sau đó tùy từng trường hợp nó sẽ gọi các hàm con xử lý tương ứng. Ví dụ : khi gặp ký tự BACK, gọi hàm xử lý phím BACK, khi gặp ký tự Z, gọi hàm xử lý việc hủy dấu,...Nếu sau khi gọi các hàm xử lý, chúng phản hổi rằng ký tự truyền vào không được hoặc không cần xử lý, hàm này sẽ gọi một hàm đặt ký tự này vào cuối bộ đệm.

Khái niệm bộ đệm ký tự : Bộ đệm ký tự là một biến lưu những ký tự đã bắt được và đã qua xử lý. Ví dụ : sau khi người dùng nhập "caau lacj booj VB" và chương trình đã xử lý thì nội dung bộ đệm sẽ là "câu lạc bộ VB". Mục đích của bộ đệm này là để đồng bộ giữa các ký tự đang hiển thị trên cửa sổ của ứng dụng đang nhập văn bản, từ đó để chương trình của ta xử lý chính xác. Do đó, ta luôn phải kiểm tra để đồng bộ nội dung bộ đệm này với những gì đang hiển thị ngoài cửa sổ nhập văn bản. Lấy ví dụ : Trên cửa sổ Notepad đang có "câu lac" và người dùng nhập "j", tất nhiên ta xử lý và được "câu lạc", sau đó ta xuất bộ đệm "câu lạc" trở lại cửa sổ Notepad là "câu lạc". Tuy nhiên, nêu người dùng không nhập "j" ngay mà lại sang nhấn nút sang trái 2 lần rồi mới nhập "j" mục đích là để được "cậu". Lúc này ta phải kiểm tra và di chuyển giữa các ký tự trong bộ đệm sao cho chính xác như vị trí trên cửa sổ Notepad. Việc này đôi khi rất khó xác định và với tôi, tôi quyết định xóa bộ đệm và bắt đầu bắt lại các ký tự khi người dùng nhấn những phím dạng TAB hay di chuyển vị trí (LEFT,RIGHT,HOME,END,...) để an toàn, tránh bị lộn xộn giữa bộ đệm của chương trình chúng ta và chuỗi thực tế đang hiển thị trên cửa sổ ứng dụng. Các bạn có thể nghiên cứu để có những cách xử lý hiệu quả hơn.

Trở lại với hàm ProcessChar, như đã nói, hàm này có nhiệm vụ phân loại các trường hợp từ đó gọi các phương thức xử lý tương ứng.
Đầu tiên các bạn khai báo thêm một biến "m_Processed" vào phần đầu của module như sau :
  1. Private m_Processed                 As Boolean

Biến này có tác dụng để đánh dấu rằng sau khi gọi các hàm xử lý, ký tự truyền vào có được xử lý hay chưa. Nếu ký tự truyền vào các hàm đó không được xử lý thì hàm này sẽ gọi một hàm khác đặt ký tự vào cuối bộ đệm. Ví dụ : Khi bộ đệm đang là "câu lạc bộ " và người dùng nhập chữ "V" thì ta không cần phải xử lý gì mà chỉ đặt vào cuối bộ đệm để đồng bộ với cửa sổ của ứng dụng mà thôi.
Tại đầu hàm này (ProcessChar), ta khởi tạo giá trị biến "m_Processed = False".
Ta sẽ phân ra các trường hợp cụ thể cần xử lý và các hàm xử lý tương ứng như sau (Tên hàm bằng English có thể không chính xác do English của tui hơi cùi) :
- Phím BACK : ProcessBackKey()
- Ký tự tạo dấu hỏi : ProcessQuestion_MarkKey()
- Ký tự bỏ dấu : ProcessRemove_MarkKey()
- Ký tự tạo dấu ngã : ProcessTilde_markKey()
- Ký tự tạo dấu sắc : ProcessACut_MarkKey()
- Ký tự tạo dấu huyền : ProcessGrave_MarkKey()
- Ký tự tạo dấu nặng : ProcessDrop_ToneKey()
- Ký tự tạo dấu trăng của chữ Ă : ProcessA_BracketKey()
- Ký tự tạo dấu mũ của Â: ProcessA_CircumFlexKey()
- Ký tự tạo dấu gạch ngang của chữ Đ : ProcessD_DashKey()
- Ký tự tạo dấu mũ của Ê : ProcessE_CircumFlexKey()
- Ký tự tạo dấu móc của Ơ : ProcessO_BracketKey()
- Ký tự tạo dấu mũ của Ô : ProcessO_CircumFlexKey()
- Ký tự tạo dấu móc của Ư : ProcessU_BracketKey()
- Đặt ký tự vào cuối bộ đệm : PutToBuffer()

Chú ý : Sau này, khi các bạn nắm rõ, các bạn có thể gộp việc xử lý vào một vài hàm hoặc tách nhỏ thành các hàm con tùy ý.

Để cài đặt Code cho hàm này các bạn cần khai báo thêm :
  1. Private Type VIETKEY_CONFIG
  2.     A_CircumFlex As String * 1          'Dâu mu~ cu?a Â
  3.    E_CircumFlex As String * 1          'Dâu mu~ cu?a Ê
  4.    O_CircumFlex As String * 1          'Dâu mu~ cu?a Ô
  5.    A_Bracket As String * 1             'Dâu móc cu?a A(
  6.    O_Bracket As String * 1             'Dâu móc cu?a O*
  7.    U_Bracket As String * 1             'Dâu móc cu?a U*
  8.    D_Dash As String * 1                'Dau gach cu?a -D
  9.    Grave_Mark As String * 1            'Dâu huyên
  10.    ACut_Mark As String * 1             'Dâu sa('c
  11.    Question_Mark As String * 1         'Dâu ho?i
  12.    Tilde_Mark As String * 1            'Dâu nga~
  13.    DropTone_Mark As String * 1         'Dâu na(.ng
  14.    Remove_Mark As String * 1           'Bo? dâu
  15. End Type
  16.  
  17. Private m_VietkeyConfig             As VIETKEY_CONFIG
  18.  


Tôi đã nhóm các ký tự cần xử lý vào một kiểu cấu trức (Type) cho dễ dàng lưu trữ và sử dụng. Mỗi khi chuyển kiểu gõ, ta lại Fill (điền) các ký tự tương ứng vào cấu trúc này.
Mã cài đặt hàm ProcessChar :
  1. Private Sub ProcessChar(ByVal lpChar As Long)
  2.     m_Processed = False
  3.     Dim sChar As String
  4.    
  5.     '
  6.    If lpChar = VK_BACK Then
  7.         '--------------------------
  8.        'Call ProcessBackKey(lpChar)
  9.        '--------------------------
  10.    ElseIf lpChar = VK_ESCAPE Then
  11.         '----------------------------
  12.        'Call ProcessEscapeKey(lpChar)
  13.        '----------------------------
  14.    ElseIf lpChar = VK_RETURN Then
  15.         '-----------------------------
  16.        'Call ProcessReturnKey(lpChar)
  17.        '-----------------------------
  18.    ElseIf lpChar = VK_TAB Then
  19.         '--------------------------
  20.        'Call ProcessTabKey(lpChar)
  21.        '--------------------------
  22.    Else
  23.         sChar = Chr(lpChar)
  24.         If StrComp(sChar, m_VietkeyConfig.Question_Mark, vbTextCompare) = 0 Then
  25.             '-------------------------------------
  26.            'Call ProcessQuestion_MarkKey(lpChar)
  27.            '-------------------------------------
  28.        End If
  29.         If Not m_Processed Then
  30.             If StrComp(sChar, m_VietkeyConfig.Remove_Mark, vbTextCompare) = 0 Then
  31.                 '----------------------------------
  32.                'Call ProcessRemove_MarkKey(lpChar)
  33.                '----------------------------------
  34.            End If
  35.         End If
  36.         If Not m_Processed Then
  37.             If StrComp(sChar, m_VietkeyConfig.Tilde_Mark, vbTextCompare) = 0 Then
  38.                 '---------------------------------
  39.                'Call ProcessTilde_markKey(lpChar)
  40.                '---------------------------------
  41.            End If
  42.         End If
  43.         If Not m_Processed Then
  44.             If StrComp(sChar, m_VietkeyConfig.ACut_Mark, vbTextCompare) = 0 Then
  45.                 '-------------------------------
  46.                'Call ProcessACut_MarkKey(lpChar)
  47.                '-------------------------------
  48.            End If
  49.         End If
  50.         If Not m_Processed Then
  51.             If StrComp(sChar, m_VietkeyConfig.Grave_Mark, vbTextCompare) = 0 Then
  52.                 '---------------------------------
  53.                'Call ProcessGrave_MarkKey(lpChar)
  54.                '---------------------------------
  55.            End If
  56.         End If
  57.         If Not m_Processed Then
  58.             If StrComp(sChar, m_VietkeyConfig.DropTone_Mark, vbTextCompare) = 0 Then
  59.                 '-------------------------------
  60.                'Call ProcessDrop_ToneKey(lpChar)
  61.                '-------------------------------
  62.            End If
  63.         End If
  64.         If Not m_Processed Then
  65.             If StrComp(sChar, m_VietkeyConfig.A_Bracket, vbTextCompare) = 0 Then
  66.                 '-------------------------------
  67.                'Call ProcessA_BracketKey(lpChar)
  68.                '-------------------------------
  69.            End If
  70.         End If
  71.         If Not m_Processed Then
  72.             If StrComp(sChar, m_VietkeyConfig.A_CircumFlex, vbTextCompare) = 0 Then
  73.                 '-------------------------------
  74.                'Call ProcessA_CircumFlexKey(lpChar)
  75.                '-------------------------------
  76.            End If
  77.         End If
  78.         If Not m_Processed Then
  79.             If StrComp(sChar, m_VietkeyConfig.D_Dash, vbTextCompare) = 0 Then
  80.                 '-------------------------------
  81.                'Call ProcessD_DashKey(lpChar)
  82.                '-------------------------------
  83.            End If
  84.         End If
  85.         If Not m_Processed Then
  86.             If StrComp(sChar, m_VietkeyConfig.E_CircumFlex, vbTextCompare) = 0 Then
  87.                 '-------------------------------
  88.                'Call ProcessE_CircumFlexKey(lpChar)
  89.                '-------------------------------
  90.            End If
  91.         End If
  92.         If Not m_Processed Then
  93.             If StrComp(sChar, m_VietkeyConfig.O_Bracket, vbTextCompare) = 0 Then
  94.                 '-------------------------------
  95.                'Call ProcessO_BracketKey(lpChar)
  96.                '-------------------------------
  97.            End If
  98.         End If
  99.         If Not m_Processed Then
  100.             If StrComp(sChar, m_VietkeyConfig.O_CircumFlex, vbTextCompare) = 0 Then
  101.                 '-------------------------------
  102.                'Call ProcessO_CircumFlexKey(lpChar)
  103.                '-------------------------------
  104.            End If
  105.         End If
  106.         If Not m_Processed Then
  107.             If StrComp(sChar, m_VietkeyConfig.U_Bracket, vbTextCompare) = 0 Then
  108.                 '-------------------------------
  109.                'Call ProcessU_BracketKey(lpChar)
  110.                '-------------------------------
  111.            End If
  112.         End If
  113.        
  114.         If Not m_Processed Then
  115.             '-------------------------------
  116.            'Call PutToBuffer(lpChar)
  117.            '-------------------------------
  118.        End If
  119.     End If
  120. End Sub



b. Xử lý phím BACK
Hàm này được hàm ProcessChar gọi khi bắt gặp phím BACK được nhấn. Mục đích của hàm này là để đồng bộ giữa bộ đệm ký tự với văn bản hiển thị trên cửa sổ ứng dụng. Ví dụ : Trên cửa sổ Notepad đang có "câu lạc bộ" và trong chuỗi bộ đệm hiện tại cũng tương tự, người dùng nhấn phím BACK để xóa 1 ký tự để được "câu lạc b" thì chương trình của chúng ta cũng cần xóa bớt một ký tự cuối trong bộ đệm để tương ứng với những gì đang hiển thị trên cửa sổ notepad.
Đầu tiên, ta phải kiểm tra xem phím BACK là do người dùng nhấn, hay do chương trình của chúng ta tạo ra để xóa các ký tự trên cửa sổ ứng dụng. Nếu là phím BACK do chương trình của chúng ta tạo ra thì phải thoát khỏi hàm ngay. Nếu phím BACK do người dùng nhấn thì ta mới xử lý tiếp.
Tiếp đến, ta phải kiểm tra xem trong chuỗi bộ đệm có rỗng không thì mới cần xóa các ký tự, nếu rỗng thì thoát khỏi hàm.

Để cài đặt hàm này, đầu tiên các bạn cần thêm 2 biến:
  1. Private m_BufferString              As String
  2. Private m_BufferArray()             As Long


Biến "m_BufferString" chứa bộ đệm ký tự dạng ký tự
Biến "m_BufferArray" chứa bộ đệm theo dạng mảng các mã của các ký tự

Mã cài đặt hàm ProcessBackKey

  1. Private Sub ProcessBackKey(ByVal lpChar As Long)
  2.     'Process for BackSpace key
  3.    If m_IsMyBack Then Exit Sub
  4.     If m_KeyCounts > 0 Then
  5.         m_KeyCounts = m_KeyCounts - 1
  6.         If m_KeyCounts > 0 Then
  7.             'Resize buffer
  8.            m_BufferString = Left(m_BufferString, m_KeyCounts)
  9.             ReDim Preserve m_BufferArray(m_KeyCounts)
  10.         Else
  11.             '-------------------
  12.            'Call ClearBuffer
  13.            '-------------------
  14.        End If
  15.     End If
  16. End Sub


c. Xử lý ký tự tạo dấu hỏi
Đối với việc xử lý các ký tự dấu nói chung, ta chỉ cần duyệt 1 từ cuối cùng trong chuỗi bộ đệm để xử lý mà thôi. Ví dụ : Trong bộ đệm là chuỗi "Câu lạc bo", khi người dùng gõ thêm một ký tự nào đó (lấy ví dụ chữ o) thì ta chỉ cần xử lý cho từ "bo" mà thôi, mà không phải duyệt hết từ đầu đến cuối bộ đệm vì đôi khi chuỗi bộ đệm có thể lên tới hàng chục thậm chí là hàng trăm ký tự.
Do đó, ta nên viết thêm một hàm riêng để lấy về vị trí bắt đầu của từ cuối cùng trong bộ đệm.
Vị trí bắt đầu của một từ được xác định là vị trí đầu tiên của bộ đệm hoặc sau các ký tự phân cách (,.:/,, v.v...) hoặc sau các ký tự số (0,1,2,...)

Để cài đặt hàm này ta định nghĩa ra một hằng (Const) chứa những giá trị mà ta cho là dấu phân cách để dựa vào dó hàm sẽ xác định vị trí bắt dầu một Từ, cộng thêm một hằng để xác định độ dài tối đa cho một từ:
  1. Private Const WORDS_SEPARATOR = ",.:;"" ~`1!2@3#4%6^7&8*9(0)-_=+[{]};:',<.>/?JZ" & vbCrLf
  2. Private Const WORD_MAX_LENGTH = 6
  3.  


Cài đặt hàm :
  1. Private Function GetLastWordStartPosition() As Long
  2.     If m_KeyCounts <= 0 Then
  3.         GetLastWordStartPosition = 0
  4.         Exit Function
  5.     End If
  6.     Dim sChar As String, dwIndex As Long
  7.     For dwIndex = m_KeyCounts To 1 Step -1
  8.         sChar = Mid(m_BufferString, dwIndex, 1)
  9.         If (InStrRev(WORDS_SEPARATOR, sChar) > 0) Or ((m_KeyCounts - dwIndex - 1) >= WORD_MAX_LENGTH) Then
  10.             GetLastWordStartPosition = dwIndex
  11.             Exit Function
  12.         End If
  13.     Next dwIndex
  14.     GetLastWordStartPosition = 1
  15. End Function


Trở lại hàm ProcessQuestion_MarkKey, đầu tiên ta lấy về vị trí bắt đầu của từ cuối cùng trong bộ đệm để xử lý bằng cách gọi hàm GetLastWordStartPosition ở trên.
Sau đó ta duyệt trong Từ cuối cùng để kiểm tra xem có bất kỳ ký tự nào trong đó có thể kết hợp với ký tự tạo dấu hỏi hay không. Ví dụ : Từ cuối cùng của bộ đệm là "câu", chắc chắn khi chữ "â" kết hợp với dấu hỏi sẽ tạo ra từ có nghĩa (ẩ). Nhưng nếu sau "â" là chữ "t" thì lại không được.
Nếu không có ký tự nào có thể kết hợp được thì ta thoát khỏi hàm. Khi đó, hàm ProcessChar sẽ kiểm tra biến m_Processed và thấy giá trị của biến này chưa được đặt = TRUE nên nó sẽ gọi hàm PutToBuffer để đặt ký tự tạo dấu hỏi vào tiếp sau bộ đệm.
Hầu hết mọi trường hợp, chỉ những nguyên âm mới có thể kết hợp với các ký tự tạo dấu

Tiếp đến, khi đã tìm thấy một ký tự có thể kết hợp, ta tiếp tục kiểm tra sự kết hợp có thể tạo ra ký tự có nghĩa và hợp lệ hay không. Ví dụ : Không thể có "Qủ" hay "qủa", "gìanh" mà xử lý đúng phải là "Qur", quả" và "giành".
Sau khi xử lý được kết quả ta xây dựng lại bộ đệm ký tự, và tách ra những ký tự cần xuất ra cửa sổ ứng dụng để thay thế. Ví dụ : Sau khi xử lý "caua" thành "câu" thì ta sẽ cần xóa 3 ký tự "aua" trên cửa sổ ứng dụng đi và thay thế bằng "âu" để được kết quả như người dùng mong muốn. Ta không cần phải xóa trọn cả 1 từ để thay bằng cả một từ mới (ví dụ : không cần xóa cả "caua" để thay bằng "câu") mà chỉ cần chọn ra những ký tự cần thiết. Thường là tất cả những ký tự cuối cùng tính từ ký tự được xử lý.

Trước khi cài đặt hàm này, ta sẽ tạo một số hàm tiện ích để chuyển qua lại giữa những ký tự có dấu thành không dấu vào hàm dựng lại bộ đệm ký tự:
  1. Private Function FromOutput(ByVal sToConvert) As String
  2.     If Len(sToConvert) <= 0 Then
  3.         Exit Function
  4.     End If
  5.    
  6.     Dim sResult As String, I As Long
  7.     sResult = CStr(sToConvert)
  8.     For I = LBound(m_OUTPUT) To UBound(m_OUTPUT)
  9.         sResult = Replace(sResult, m_OUTPUT(I), m_INPUT(I))
  10.     Next I
  11.     FromOutput = sResult
  12. End Function
  13.  
  14. Private Function ToOutput(ByVal sToConvert As String) As String
  15.     If Len(sToConvert) <= 0 Then
  16.         Exit Function
  17.     End If
  18.     Dim sResult As String, sTam As String, I As Long, J As Long, bFound As Long
  19.     bFound = 1
  20.     sResult = CStr(sToConvert)
  21. Restart:
  22.     For I = bFound To Len(sResult)
  23.         bFound = False
  24.         For J = UBound(m_INPUT) To LBound(m_INPUT) Step -1
  25.             If (StrComp(m_INPUT(J), Mid(sResult, I, Len(m_INPUT(J))), vbTextCompare) = 0) And (StrComp(Left(m_INPUT(J), 1), Mid(sResult, I, 1), vbBinaryCompare) = 0) Then
  26.                 sTam = Left(sResult, I - 1) & m_OUTPUT(J) & Mid(sResult, I + Len(m_INPUT(J)))
  27.                 sResult = sTam
  28.                 bFound = I + Len(m_OUTPUT(J))
  29.                 GoTo Restart
  30.                 Exit For
  31.             End If
  32.         Next J
  33.     Next I
  34.     ToOutput = sResult
  35. End Function
  36.  
  37. Private Sub RebuildBufferString()
  38.     m_BufferString = ""
  39.     If m_KeyCounts <= 0 Then Exit Sub
  40.     Dim I As Long
  41.     For I = 1 To m_KeyCounts
  42.         m_BufferString = m_BufferString & ChrW$(m_BufferArray(I - 1))
  43.     Next I
  44. End Sub
  45. '
  46. Private Sub ReBuildBufferArray()
  47.     Erase m_BufferArray
  48.     If m_KeyCounts <= 0 Then Exit Sub
  49.     Dim I As Long
  50.     For I = 1 To m_KeyCounts
  51.         ReDim Preserve m_BufferArray(I - 1)
  52.         m_BufferArray(I - 1) = AscW(Mid$(m_BufferString, I, 1))
  53.     Next I
  54. End Sub


Hỏi ý kiến tác giả trước khi sử dụng hoặc trích dẫn rõ nguồn bài viết khi sử dụng.

Quay về “[VB] Bài viết hướng dẫn”

Đang trực tuyến

Đang xem chuyên mục này: Không có thành viên nào trực tuyến.0 khách