Checking the spelling in a given string, such as the text in a control, is something that a lot of Visual FoxPro applications can use. There are many ways to accomplish this.  Some Visual FoxPro developers use Microsoft Word similar to the way I did in my FAQ about it. Others shell out between $250-500 in order to purchase a third-party tool or control that will get the job done. And some, just do without.

Making a Contribution
Well, a couple days ago I was looking for a way to put my recent blog about the Visual FoxPro Community into action at this end, and I decided to try my hand at creating a class with the ability to easily add spelling checks to Visual FoxPro applications. Here are some of the important steps I took, followed by a screen capture of spelling checker I created in Visual FoxPro and a couple of links, so you can download a copy of the project (all source code included), some runnable examples, and even an alternate dictionary table.

Step 1:
Find a way to recognize a misspelling. Obviously I needed a dictionary. Luckily there are plenty of open source dictionaries out there, and I soon I had compiled a dictionary table of 144,238 words. The words came from Kevin Atkinson’s Spell Checking Oriented Word Lists (SCOWL). I took English and American words up to level 70. Level 80 and 95 words are mostly obscure (scrabble players only), removed possessive words and duplicates, and gave it a few final tweaks. 140,000+ words is a nice size dictionary for any spelling checker.

Step 2:
Give the user some choices of possible matches. Here again I was lucky in that I’ve worked on a number of sounds-like algorithms for Visual FoxPro in the past (including: Improved Soundex, Metaphone, and Double Metaphone). In this case, because I was dealing with English words the Improved Soundex was all that was needed, and it is the fastest of the alternatives so that was a Win-Win. However, this only handled misspellings that sound like the actual spelling, if I ran into “thwrw” because the user’s finger had slipped to the “w” instead of the “e”, the Improved Soundex algorithm wasn’t going to do me much good.

Well, it just so happened that I had run into a posting of a class method for computing the Levenshtein Distance in Visual FoxPro and I had rewritten it (see the bottom of the page I linked). The Levenshtein Distance is an excellent indicator of how closely matched two words are even though they don’t sound the same.

Step 3:
With the first two hurdles overcome, it was time to figure out what kind of design I would go for. I decided on doing the entire thing with classes and in such a way that new user interfaces could be created and hooked up. Now, bear in mind that this is just my first stab at it and less than two days have gone by, but I think you’ll see some things you like if you care enough to dig into it.

Before I go any further let me say that I have not put the error handling into it yet. That will go in next. I find with projects of this size (and when there is a high potential for me to be changing my mind while coding) that it is easiest to put the error handling in once the design has stabilized. So please, no flames about the lack of any error handling… it’s on the way. Or, better yet, since it will be a couple of days before I can get back to this, how about you throw in some really nice Try…Catch…Finally commands in it and shoot it back to me? LOL.

The guts of the spelling checker is in the spellcheck class in spellcheck.vcx and my first stab at an interface is the splchkdialog class in spellcheck.vcx.  Other than that the class library just holds a bunch of subclasses of the Visual FoxPro base classes. I want to encapsulate the logic even further, so interfaces are a complete snap to create and the spellcheck class is a black box engine (but it’s still pretty decent as it sits now).

Step 4:
Return a list of suggestions for the user to pick from as replacements for a given misspelling really fast. I had to figure out a way to tweak the returned suggestion list that would handle the size of the dictionary table. I decided on an SQL select statement because of its flexibility and speed when optimized. Then I defined the criteria for the SQL where clause. I also ordered the elements in the Where clause so that the criteria that would weed out the most entries came first and the most resource intensive operations (such as the Levenshtein Distance) would come last.

This still wasn’t fast enough, so I created another field in the dictionary table to hold the Improved Soundex codes for the words and indexed it. And created an index on the LEN(ALLTRIM()) of the word as well. This dramatically improved the speed and after a few more tweaks I had a workable SQL for returning the results. See the getsuggestions method of the spellcheck class.

Step 5:
Logically order the results returned to the user. Alphabetical returns are anything but logical when returning suggestions for a spellchecker. I decided to add a weight field to the suggestions curosr and base it on different aspects of the matches between the word in question and the words in the dictionary table. In this case, I went with lower weights meaning better the matches. The weight made an excellent field to base the Order by clause on as well. The top 10 results were finally being returned in a logical order. To see the way the weight is figured, look in the figureweight method of the spellcheck class.

Step 6:
Test, tweak, test fix… drink more coffee and Red Bull energy drink… Test, tweak, test, fix. And finally, create two examples, write a README.txt, zip it all up, take a couple screen shots, and write this blog. The download link is provided below (look on the right-hand side), full source included, don’t forget to read the README.txt.

Visual FoxPro Spelling Checker

 Download VFP Spelling Checker (2MB approx.)

UPDATES: 07-31-2005 Created a new dictionary table based on the SCOWL so that copyright and redistribution rights were sure (removed previous two dictionaries). Optimized some code, fixed a couple of bugs, worked in the new dictionary and edited this blog entry to apply to the new dictionary table.