Logo of Vovisoft

SpellCheck một Text String

K iểm lỗi chính tả là một thách thức thú vị về kỹ thuật lập trình. Nó đòi hỏi sự tính toán về thiết kế và về cách viết code.
Như ta thường dùng công cụ SpellCheck trong các chương trình thông dụng như Word, Excell .v.v.. nó hoạt động coi êm ái, dễ dàng, nhưng thật ra nó không đơn giản đâu. Chính Microsoft đã từng license kỹ thuật SpellCheck từ một công ty khác để chuyên lo công việc nầy cho Microsoft.
Ở đây ta thử bàn bạc và thiết kế một phương tiện SpellCheck nho nhỏ để có thể dùng trong chính các programs cây nhà, lá vườn của mình.

Program SpellCheck bằng VB6

Thông thường User có một Text String mà họ vừa mới đánh vào.

Họ sẽ Click một Menu Command hay một nút SpellCheck. Lập tức một SpellCheck Dialog sẽ hiện ra giống như sau:

Program có sẵn một tự điển ( Dictionary) của tất cả những chữ đúng chính tả. Khi SpellCheck, nó sẽ kiểm từng chữ một trong Text String xem chữ ấy có mặt trong tự điển không. Nếu có thì nó sẽ lướt qua đến một chữ khác.
Khi gặp một chữ chưa có trong tự điển, program sẽ highlight chữ ấy trong TextBox và display những chữ trong tự điển có spelling tương đối gần giống chữ ấy nhất. Program sẽ đợi, và User có 3 options:
  • Skip chữ ấy: Bảo program đừng quan tâm đến chữ ấy và tiếp tục xử lý chữ kế tiếp.
  • Add chữ ấy vào tự điển: Ðây là cách cho tự điển học thêm chữ mới.
  • Change chữ ấy: Thay thế chữ ấy trong Text String bằng hoặc là một chữ nằm trong danh sách các chữ đã được đề nghị (thường thường các chữ nầy chỉ trật một character so với chữ ta đang xử lý, có lẽ vì chữ ta đang xử lý bị đánh vần trật) hoặc một chữ mới hoàn toàn mà User mới đánh cái TextBox nằm phía trên.
Sau khi User đã chọn một trong 3 options kể trên, program sẽ tiếp tục (continue) cho đến khi đến cuối Text String.
Kết cục, ta sẽ có một Text String đã đuợc SpellChecked (sửa lỗi chính tả) và một Dictionary đuợc cho thêm một số chữ mới để dùng trong dịp tới.

Thiết kế

Trước hết là cách chuyển TextString từ Form chánh của ta qua Form SpellCheck để nó SpellCheck và khi xử lý xong nó return Modified TextString lại cho TextBox trong Form chánh.

Kế đó là nghĩ cách chứa các Words trong Dictionary. Chứa cách nào để ta làm các việc sau đây cách hiệu năng nhất:
  • Tìm một chữ cho nhanh.
  • Display các chữ tương tợ nhanh.
  • Thêm một chữ dễ dàng và nhanh.
Cách chứa Dictionary ít tốn chỗ nhất là dùng một Tree Structure, để tùy theo việc chọn đi dọc theo các nhánh nào của cây ta có những chữ khác nhau. Cách nầy cho ta thấy rõ ràng nhất sự liên hệ của spelling các chữ tương tợ nhau. Mỗi khi ta thêm một chữ mới vào trong Dictionary có thể là thêm một nhánh vào Tree. Khi Tree Structure có sẵn trong bộ nhớ thì làm việc tiện lắm, nhưng nếu muốn lúc chứa xuống file, và đọc lên từ File cho nhanh thì hơi rắc rối.
Có thể cách nầy tính toán nhanh hơn và ít tốn bộ nhớ hơn trong ngôn ngữ lập trình khác nhưng trong VB6 nó không hẳn efficient. Dầu sau đi nữa, lập trình navigating (đi, lại đây đó) trong Tree Structure cần nhiều Recursive Subs ( những Subs gọi chính nó) và không thích hợp cho các tay non trong nghề lập trình.

Trong bài nầy, chúng ta chỉ dùng những đồ nghề và tiểu xảo có sẵn trong mình, đó là Text File, ListBoxFunction InStr.
Dictionary của chúng ta sẽ gồm có 26 hàng Text, mỗi hàng chứa tất cả những chữ bắt đầu cùng một Alphabet. Tức là hàng thứ nhất chứa những chữ bắt đầu bằng leter "a" và hàng cuối chứa những chữ bắt đầu bằng leter "z".
Khi bắt đầu ta create sẵn một Dictionary File tên words.txt với content như sau:

are
big
cut
down
entire
fall
go
hall
ink
job
Kid
large
main
narrow
old
partition
quarter
regular
small
to ten the than that thing terrible those thin
unregister
vehicle
who
x
yellow
zone

Chắc bạn đã để ý, một khi đã load content của Dictionary vào ListBox lstWords (Listbox nầy có Property Visible bằng False, tức là nó vô hình, User không thấy nó), ta có thể tìm đến đúng hàng của mỗi alphabet rất dễ dàng và nhanh. Hãy xem Function NOTFound dùng để xem một chữ có hiện diện trong Dictionary như dưới đây:
Function NOTFound(ByVal Word) As Boolean 
   ' See if the given word exists in the dictionary.
   ' if NOT then display all similar words in the suggested Listbox.
   Dim LIndex, Item 
   ' Work out the Index of the Alphabet line in the ListBox lstWords.
   ' The first line is for "a", the last line is for "z"
   LIndex = Asc(UCase(Left(Word, 1))) - Asc("A") 
   ' Prefix and Append the String with spaces.
   Item = " " & UCase(lstWords.List(LIndex)) & " " 
   lstWords.ListIndex = LIndex 
   ' We'll try to locate the Word having a blank space at either end,
   ' i.e. looking for the pattern of a whole word that is the same as given word
   Word = " " & UCase(Word) & " " 
   If InStr(Item, Word) = 0 Then 
      NOTFound = True 
      ' Display similar words
      DisplaySuggestedWords Trim(Word), lstWords.List(LIndex) 
   Else 
      NOTFound = False 
   End If 
End Function 
Ðể khỏi lẫn lộn Uppercase hay LowerCase ta luôn luôn convert text ra Uppercase trước khi so sánh. Ðể Search nguyên một chữ (whole word) ta cần phải gắn một blank space vào phía đầu và phía đuôi của một chữ, tức là để tìm chữ "big" ta phải tìm chữ " big ", nếu không nó lộn qua chữ "bigger".
Muốn thêm một chữ mới vào Dictionary, ta chỉ cần ráp nó vào cuối hàng chữ cùng starting alphabet. Ðể ngắc khúc một hàng ra nhiều chữ dễ dàng ta dùng Class clsString.

Làm sao ta tìm ra các chữ trong Dictionary tương tợ với một chữ. Ta định nghĩa hai chữ tương tợ là khi chúng chỉ khác nhau một hai letters thôi, tức là chuyện xãy ra khi có lỗi chính tả.
Ðể so sánh kiểu nầy ta lần lượt lấy từng letter trong một chữ để xem nó có mặt trong chữ kia không. Nếu có thì ta rút letter đó ra khỏi chữ kia và ghi nhận có thêm một letter giống.
Thí dụ ta có một chữ dài 6 characters. Ta sẽ chỉ so sánh nó với những chữ trong dictionaty dài từ 4(=6-2) đến 8(=6+2) characters. Mỗi khi so sánh ta lưu ý chữ ngắn hơn, giả dụ nó dài 5 characters. Như thế nếu hai chữ có ít nhất 3(=5-2) characters giống nhau thì là tương tợ.
Listing của Function SimilarWords như sau:
Function SimilarWords(ByVal Word, ByVal ILen, ByVal TWord, ByVal Tlen) As Boolean 
   ' Two words are considered Similar if the shorter word has at least (its length minus 2)
   ' characters that are found in the other word.
   ' In other words, the two words differ only a couple characters at the most.
   Dim L1, L2, W1, W2 
   Dim i, MatchCount, Pos 
   ' Identify the shorter Word and its length
   If ILen > Tlen Then 
      L1 = ILen 
      L2 = Tlen 
      W1 = UCase(Word) 
      W2 = UCase(TWord) 
   Else 
      L1 = Tlen 
      L2 = ILen 
      W1 = UCase(TWord) 
      W2 = UCase(Word) 
   End If 
   MatchCount = 0 
   ' Iterate through each character of the shorter word, i.e. W2
   For i = 1 To L2 
      ' Locate a character of W2 in W1
      Pos = InStr(W1, Mid(W2, i, 1)) 
      If Pos > 0 Then 
         ' Increment the number of same characters
         MatchCount = MatchCount + 1 
         ' Temporary remove the matched character from W1
         W1 = Left(W1, Pos - 1) & Mid(W1, Pos + 1) 
      End If 
   Next 
   If MatchCount >= (L2 - 2) Then 
      SimilarWords = True 
   Else 
      SimilarWords = False 
   End If 
End Function 
Khi liệt kê các chữ tương tợ trong ListBox lstSuggested, ta cho các chữ gần giống nhất lên đầu. Những chữ gần giống là những chữ chỉ khác có 1 hay 2 letters với chữ ta đang xử lý. Trong program ta dùng ba SORTED Listboxes TList(1), TList(2) và TList(3) để chứa các chữ có cùng chiều dài hay dài/ngắn hơn 1,2 characters (so với chữ ta đang xử lý) để chứa các chữ tương tợ tạm thời. Sau đó ta copy chúng vào lstSuggested.

Cái giới hạn của program nầy là ta chỉ so sánh một chữ với những chữ ít nhất bắt đầu cùng một alphabet. Dĩ nhiên, nếu có thì giờ bạn có thể làm cho nó hay hơn.

Bạn có thể download source code của program mẫu nầy để chạy thử.
Trong lúc tìm hiểu chương trình, bạn có thể làm cho Form frmSpell lớn ra và các ListBoxes vô hình như TList(1), TList(2), TList(3) và lstWords thành ra visible và lớn hơn. Chúng sẽ cho bạn thấy những chuyện xãy ra phía sau sân khấu.
Vì Form frmMain gọi frmSpell nên cả hai Forms cùng chạy, do đó khi muốn ngừng program thình lình trong VB6 IDE, có thể bạn cần phải click cái nút Stop Program hình vuông đen hai (2) lần.
  Học Microsoft Visual Basic 6.0