Solved

Searching for a word using wildcards

Posted on 1998-06-10
11
329 Views
Last Modified: 2010-04-04
I want to find the position of a word in a TRichEdit using the characters $ (meaning a vocal), £ (meaning a consonant), # (meaning a double consonant)  and * (meaning any number greater than zero of arbitrary letters). The search word may contain any number and combinations of these four wildcards. Thus, searching for ' *$s*'  would find the word 'close' (but not 'estimate')  and  '*$# '  would find 'fill' (but not 'fil' or 'filling').

I would like a complete algorithm or preferably a working Delphi function.
0
Comment
Question by:toreot
  • 4
  • 3
  • 2
  • +2
11 Comments
 
LVL 1

Expert Comment

by:Marcius
ID: 1351969
What do you mean by "a vocal" ? I do not understand the term
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1351970
have a look at this library :

http://www.mindspring.com/~efd/hyperstr.htm

regards, ZiF.
0
 
LVL 1

Expert Comment

by:Marcius
ID: 1351971
That looks very nice, see above, sounds like it may provide the solution.
0
 
LVL 1

Expert Comment

by:Greedy
ID: 1351972
I saw an artical in Delphi informant that did this (kinda)...It was called Soundex algorithms...they used it for a name look up so that if you had data entry order takers get someones name from them they could put it in an edit box an it would find the name even if it wasn't spelled right.  (ie Stevens and Stephens)
go to
http://www.informant.com/delphi/DI3XL.HTM 
the file name is: DI9803RS.ZIP

0
 

Author Comment

by:toreot
ID: 1351973
I will take a look as soon as possible. By the way, I should have checked my English. 'Vokal' in Norwegian is 'vowel' in English.
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 1

Expert Comment

by:Marcius
ID: 1351974
Check your example also, estimate and close both have a vowel followed by 's' ;)
0
 

Author Comment

by:toreot
ID: 1351975
Yes, but the * in front means that there should be one or more letters in front of the vowel before the s, and 'estimate' starts with the vowel. I should also make it clear that # means two IDENTICAL consonants (bb etc.).
I have looked into the hyperstring library and although it contains many useful functions, I am fairly sure they are too general to solve my problem.

If the original problem seems too daunting, the following simplication would also be useful:
At most one each of #, £, $ and at most two *, one in front and one in the back of the word. At most one substring of letters.

Now I could probably write it myself given enough time, but is there a more experienced programmer out there who takes the challenge? I may increase the points to 500.
0
 
LVL 1

Expert Comment

by:Socrates050697
ID: 1351976
I've got something similar that I'm just converting to your needs. I should have it done soon. A couple of questions :-

Do you need it to be case sensitive or not?

Are you only searching for whole words i.e.

for the sentence - 'my walls are red' and a search of  - wa#*
i assume you want the answer 'walls' not 'walls are red'

Socrates
0
 

Author Comment

by:toreot
ID: 1351977
It should not be case sensitive and  I only want to find whole words.
0
 
LVL 1

Accepted Solution

by:
Socrates050697 earned 500 total points
ID: 1351978
I was intending to get this to you yesterday but something croped up at work. Anyway here it is.

The way to use it to call 'WildSearch' sending it the Search String ( e.g. *#s),your TRichEdit control, and a TList wich you have already created. The functions return value is the number of matches found and the TList is returned with pointers to TWordRec records (defined at the top of the unit). This contains the line number, the position on that line and the length of the word that mathes your search string.
Note that the TList is initially cleared by WIldSearch.

Its a little rough and ready, but it does the job. I'm not sure how fast it is. I've tried it with a couple of hundred lines of text and it seems ok.

If you've got any other questions about this bit of code, feel free to contact me and I'll be happy to make any alterations or whatever. I'll be away all weekend so it'll either have to be today or next week. I've included the whole unit below, which includes a form with a TRichEdit, a TEdit (Called SearchEdit) and a Button which make a simple demonstration of it.

Hope this helps you out.




unit search;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls;

const
  Vowels=['A','E','I','O','U','a','e','i','o','u'];
  Consanants=['b','c','d','f','g','h','j','k','l','m','n','p','q','r',
              's','t','v','w','x','y','z','B','C','D','F','G','H','J','K','L',
              'M','N','P','Q','R','S','T','V','W','X','Y','Z'];
  WildCards=['£','$','#','*'];

type
  TWordRec = record
    Line: integer;
    Position: integer;
    Length: integer;
  end;
  PTWordRec=^TWordrec;

  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Button1: TButton;
    SearchEdit: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

function isVowel(MyChar: Char): Boolean;
function isConsanant(MyChar: Char): Boolean;
function isLetter(MyChar: Char): Boolean;
function isLetterorWildCard(MyChar: Char): Boolean;
function TextSearch(text,SearchStr: string;AtStart: Boolean;var position,Reslength: integer): Boolean;
function WildSearch(SearchStr: String;MyEdit: TRichEdit;var AList: TList): integer;


implementation

{$R *.DFM}




function TextSearch(text,SearchStr: string;AtStart: Boolean;var position,Reslength: integer): Boolean;
var
  Count, SubCount, Tester: integer;
  Continue, KeepGoing: Boolean;
  TempPos,TempLength: integer;
begin
  Count:=1;
  KeepGoing:=True;
  while KeepGoing and (Count<=Length(Text)) do
  begin
    if AtStart then KeepGoing:=False;
    SubCount:=0;
    Continue:=True;
    Tester:=1;
    while Continue and (Tester<=Length(SearchStr)) do
    begin
      Continue:=False;
      Case SearchStr[Tester] of
        // Vowel
        '$' : begin
          if isVowel(Text[Count+SubCount]) then
          begin
            Continue:=True;
            inc(Tester);
            inc(SubCount);
          end;
        end;
        // Consanant
        '£' : begin
          if isConsanant(Text[Count+SubCount]) then
          begin
            Continue:=True;
            inc(Tester);
            inc(SubCount);
          end;
        end;
        // Double consanant
        '#' : begin
          if isConsanant(Text[Count+SubCount]) and (AnsiLowerCase(Text[Count+SubCount])=AnsiLowerCase(Text[Count+SubCount+1])) then
          begin
            Continue:=True;
            inc(Tester,1);
            inc(SubCount,2);
          end;
        end;
        // Some Letters
        '*' : begin
            inc(Tester);
          if Tester>Length(SearchStr) then
          begin
            Continue:=True;
            Tester:=Length(SearchStr)+1;
            if SubCount=(Length(Text)-Count+1) then
              Continue:=False
            else
              SubCount:=Length(Text)-Count+1;
          end
          else if TextSearch(Copy(Text,Count+SubCount+1,Length(Text)-(Count+SubCount)),Copy(SearchStr,Tester,Length(SearchStr)-Tester+1),False,TempPos,TempLength) then
          begin
            Continue:=True;
            Tester:=Length(SearchStr)+1;
            SubCount:=SubCount+TempLength+TempPos;
          end;
        end;
        else begin
          if AnsiLowerCase(Text[Count+SubCount])=AnsiLowerCase(SearchStr[Tester]) then
          begin
            Continue:=True;
            inc(Tester);
            inc(SubCount);
          end;
        end;
      end;
    end;
    if AtStart and ((SubCount)<Length(Text)) then Continue:=False;
    if Continue then
    begin
      Position:=Count;
      ResLength:=SubCount;
      Result:=True;
      exit;
    end;
    inc(Count);
  end;
  Result:=False;
  Position:=0;
  ResLength:=0;
end;

function isVowel(MyChar: Char): Boolean;
begin
  If MyChar in Vowels then
    result:=True
  else
    result:=False;
end;

function isConsanant(MyChar: Char): Boolean;
begin
  If MyChar in Consanants then
    result:=True
  else
    result:=False;
end;

function isLetter(MyChar: Char): Boolean;
begin
  if isVowel(MyChar) or isConsanant(MyChar) then
    result:=True
  else
    result:=False;
end;

function isLetterOrWildCard(MyChar: Char): Boolean;
begin
  if isLetter(MyChar) or (MyChar in WildCards) then
    result:=True
  else
    result:=False;
end;

function WildSearch(SearchStr: String;MyEdit: TRichEdit;var AList: TList): integer;
var
  TestStr: string;
  WordStart,WordEnd: integer;
  TestWord: string;
  n,m: integer;
  Position,ResLength: integer;
  WordRec: PTWordRec;
begin
  AList.Clear;
  For n:=0 to MyEdit.Lines.Count-1 do
  begin
    TestStr:=MyEdit.Lines[n];
    if TestStr<>'' then
    begin
      TestWord:='';
      WordStart:=1;
      While not isLetter(TestStr[WordStart]) do
        inc(WordStart);
      For m:=WordStart to Length(TestStr) do
      begin
        if isLetter(TestStr[m]) then
        begin
          TestWord:=TestWord+TestStr[m];
          WordEnd:=m;
          if (m=Length(TestStr)) or (not isLetter(TestStr[m+1])) then
          begin
            if TextSearch(TestWord,SearchStr,True,Position,ResLength) then
            begin
              New(WordRec);
              WordRec.Line:=n;
              WordRec.Position:=WordStart;
              WordRec.Length:=WordEnd-WordStart+1;
              AList.Add(WordRec);
            end;
            WordStart:=WordEnd;
            TestWord:='';
          end;
        end
        else
          WordStart:=m+1;
      end;
    end;
  end;
  Result:=AList.Count;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  MyList: TList;
  Number,n: integer;
  WordRec: PTWordRec;
begin
  MyList:=TList.Create;
  try
    Number:=WildSearch(SearchEdit.Text,RichEdit1,MyList);
    for n:=0 to Number-1 do
    begin
      WordRec:=MyList[n];
      ShowMessage(Copy(RichEdit1.Lines[WordRec.Line],WordRec.Position,WordRec.Length));
    end;
  finally
    MyList.Free;
  end;
end;

0
 

Author Comment

by:toreot
ID: 1351979
It seems to work beautifully. Just what I needed. I will give you 500 points.
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, just open a new email message. In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
Both in life and business – not all partnerships are created equal. As the demand for cloud services increases, so do the number of self-proclaimed cloud partners. Asking the right questions up front in the partnership, will enable both parties …

911 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now