Solved

Searching for a word using wildcards

Posted on 1998-06-10
11
334 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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
 
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

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

Suggested Solutions

Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
In an interesting question (https://www.experts-exchange.com/questions/29008360/) here at Experts Exchange, a member asked how to split a single image into multiple images. The primary usage for this is to place many photographs on a flatbed scanner…

730 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