Case-Sensitive Word Finder by Seth Willits
01-24-04




Word Finding
A lot of applications have "Find" windows. (Any decent text editor does, and even some not-so decent ones do.) One feature a lot these Find windows don't have is the ability to do case-sensitive searching. That is, make a distinction between TEST and Test as well as test and TeSt. REALbasic it self doesn't have this. Another bummer about a lot of applications is that they don't find matching words. So when searching for "tEsT", I would find the very first four letters ("Test") in the phrase "Testing my tEsT is tEsTing" as well as the word "tEsT" and the first portion of the last word "tEsT". In this tutorial, we'll build a short and simple module to find matching words instead of pieces of words so we would only find the single alone occurence of the word "tEsT".


Design
As usual, the concept is pretty simple. There are basically four steps to doing this:

That's all there is to it. Create a module named "WordMatching" or whatever you want, and create a method inside of it: MatchWord(source as string, find as string, start as integer, sensitive as boolean) As integer

After that, just create a window with two fields and a button labeled "Find". (The example project has this all done for you.) The EFBody EditField will be our "source" field and the EFSearch EditField will containt the word we're looking for. Obviously, the Find button initiates the search.

The MatchWord Function
The first parameter the string we'll be searching in, the second is the word we're looking for, and the last is a boolean value representing whether the search should be case-sensitive or not. The first step is to set up some temporary variables to aid us in the matching. length is the length of the "find" string, theEnd is offset that the word would be at if it was at the very end of source, ("theStart" is just 1), and Allowable is a list of allowable characters to separate words. (Note, these are NOT allowed IN the words. I know it's a bad name, but I'm too lazy to go back and change it now.)

After that we're going to enter a loop which keeps executing while possible matches are found. If no match is made or no possible matches are found, the loop will exit. The first line of the loop checks for any possible matches. The next three lines then check to see if the case of the find string and the match are the same. StrComp is a built-in function in REALbasic which does case-sensitive string comparision. If StrComp returns 0, then the strings are equivalent. If the case does not match and case-sensitivity is on, the offset value is set to 0 signalling that it is not a match, and that the loop should iterate once more to see if a new match can be found.

After that, we check the characters immediately to the left and right of the match to see whether or not they are allowable word separators. If they are, then we've got a match and the loop exits since offset does not equal zero. Note that we check for offset to be > 1 when checking left and offset > 0 and offset <= theEnd when checking right. This not only checks to see if the word is at the edges of the field (because if it is, it is automatically a match for that respective side), but it also makes sure there's a possible match since offset must not be 0 for either testing to occur.

Function MatchWord(source as string, find as string, start as integer, sensitive as boolean) As integer
   dim offset, length, theEnd as integer
   dim Allowable as String
   
   
   // Setup
   length = Len(find)
   theEnd = Len(source) - length
   Allowable = ".,:;/\<>()-+='""?![]{}" + " " + chr(9) + chr(13) + chr(10)
   
   // Simply find it
   do
      offset = InStr(start, source, find)
      
      
      // Matches case?
      if offset > 0 and Sensitive and StrComp(find, Mid(source, offset, length), 0) <> 0 then
         offset = 0
      end if
      
      
      // Check left
      if offset > 1 then
         if InStr(Allowable, Mid(source, offset-1, 1)) = 0 then
            offset = 0
         end if
      end if
      
      
      // Check Right
      if offset > 0 and offset <= theEnd then
         if InStr(Allowable, Mid(source, offset + length, 1)) = 0 then
            offset = 0
         end if
      end if
      
      start = start + length
   loop until offset <> 0 or start >= theEnd
   
   return offset
End Function



The Find Button
The code in the Find button is just an example of how to use the MatchWord function in an EditField. It should be self explanitory from the comments.

Sub Action()
dim offset as Integer

// Make sure we'll be selecting the next match and not the current one
EFText.SelStart = EFText.SelStart + EFText.SelLength
EFText.SelLength = 0


// Find offset of match
offset = MatchWord(EFText.Text, EFSearch.Text, EFText.SelStart, ChkCase.Value)

// Select the match
if offset <> 0 then
lastOffset = offset
EFText.SelStart = offset - 1
EFText.SelLength = Len(EFSearch.Text)
end if
End Sub

The Finished Product
That's all there is to it! You can download the project here.