DelphiFAQ Home Search:

Compare two strings with wildcards (*, ?)

 

comments14 comments. Current rating: 4 stars (4 votes). Leave comments and/ or rate it.

If you need to compare two strings where one contains wildcards (just * and ?), you may find the following useful - I used it years ago in a Turbo Pascal project.

It was a DOS program.. that's why it contains those type definitions at the top.
In Delphi 2/3 you may replace them just with string - otherwise you'll need to set the compiler syntax option "Strict Var-Strings" OFF = {$V-}.

The part at the bottom shows how to use the routine WildComp.

type
  PathStr = string[128]; { in Delphi 2/3: = string }
  NameStr = string[12];  { in Delphi 2/3: = string }
  ExtStr  = string[3];   { in Delphi 2/3: = string }

{$V-} { in Delphi 2/ 3 to switch off "strict var-strings" }

function WildComp(FileWild,FileIs: PathStr): boolean;
var
  NameW,NameI: NameStr;
  ExtW,ExtI: ExtStr;
  c: byte;

  function WComp(var WildS,IstS: NameStr): boolean;
  var
    i, j, l, p : Byte;
  begin
    i := 1;
    j := 1;
    while (i<=length(WildS)) do
    begin
      if WildS[i]='*' then
      begin
        if i = length(WildS) then
        begin
          WComp := true;
          exit
        end
        else
        begin
          { we need to synchronize }
          l := i+1;
          while (l < length(WildS)) and (WildS[l+1] <> '*') do
            inc (l);
          p := pos (copy (WildS, i+1, l-i), IstS);
          if p > 0 then
          begin
            j := p-1;
          end
          else
          begin
            WComp := false;
            exit;
          end;
        end;
      end
      else
      if (WildS[i]<>'?') and ((length(IstS) < i)
	  	  or (WildS[i]<>IstS[j])) then
      begin
        WComp := false;
 	exit
      end;

      inc (i);
      inc (j);
    end;
    WComp := (j > length(IstS));
  end;

begin
  c:=pos('.',FileWild);
  if c=0 then
  begin { automatically append .* }
    NameW := FileWild;
    ExtW  := '*';
  end
  else
  begin
    NameW := copy(FileWild,1,c-1);
    ExtW  := copy(FileWild,c+1,255);
  end;

  c:=pos('.',FileIs);
  if c=0 then
    c:=length(FileIs)+1;
  NameI := copy(FileIs,1,c-1);
  ExtI  := copy(FileIs,c+1,255);
  WildComp := WComp(NameW,NameI) and WComp(ExtW,ExtI);
end;

begin
  if     WildComp('a*.bmp',  'auto.bmp') then ShowMessage('OK 1');
  if not WildComp('a*x.bmp', 'auto.bmp') then ShowMessage('OK 2');
  if     WildComp('a*o.bmp', 'auto.bmp') then ShowMessage('OK 3');
  if not WildComp('a*tu.bmp','auto.bmp') then ShowMessage('OK 4');
end.

Comments:

2006-02-06, 05:56:20
anonymous from Poland  
rating
This function return wrong result for:

WildComp('*p', 'autop.bmp')

so it's not useful
2006-02-06, 08:50:04
anonymous from United States  
rating
The function was written in DOS days, and at that time

'*p' and 'autop.bmp' would not be matched by the operating system either.

You would have to use '*.*p' to match that file name.
2007-03-19, 09:44:47
SpamMe92000@yahoo.com from United Kingdom  
rating
there are 3 bugs:
    * it returns false if filter string has a double * (unless the string to match has a '*' in as well)     e.g. WildComp('a**o.b*p', 'auto.bmp') returns false instead of true
    * '*' matches a '.' in the string to match: e.g.: WildComp('auto.*', 'auto.bmp.foo') returns true instead of false
    * '*' matches only the first possible match, so WildComp('*ut.bmp', 'autout.bmp') returns false instead of true
2007-03-19, 11:30:30
thomas_kelsey@techie.com from United Kingdom  
rating
This function works better:

unit comparewild;
{Copyright (C) 2007 Thomas Kelsey

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.}


// KNOWN BUGS: accepts wildcards in target string
interface

function WildComp(const mask: String; const target: String): Boolean;

function Test: Boolean;

implementation

function WildComp(const mask: String; const target: String): Boolean;

    // '*' matches greedy & ungreedy
    // simple recursive descent parser - not fast but easy to understand
    function WComp(const maskI: Integer; const targetI: Integer): Boolean;
    begin

        if maskI > Length(mask) then begin
            Result := targetI = Length(target) + 1;
            Exit;
        end;
        if targetI > Length(target) then begin
            // unread chars in filter or would have read '#0'
            Result := False;
            Exit;
        end;

        case mask[maskI] of
            '*':
                // '*' doesnt match '.'
                if target[targetI] <> '.' then
                    // try with and without ending match - but always matches at least one char
                    Result := WComp(succ(maskI), Succ(targetI)) or WComp(maskI, Succ(targetI))
                else
                    Result := False;
            '?':
                // ? doesnt match '.'
                if target[targetI] <> '.' then
                    Result := WComp(succ(maskI), Succ(targetI))
                else
                    Result := False;

            else     // includes '.' which only matches itself
                if mask[maskI] = target[targetI] then
                    Result := WComp(succ(maskI), Succ(targetI))
                else
                    Result := False;
        end;// case

    end;

begin
    WildComp := WComp(1, 1);
end;

function Test: Boolean;
begin
Result := WildComp('a*.bmp', 'auto.bmp');
    Result := Result and (not WildComp('a*x.bmp', 'auto.bmp'));
    Result := Result and WildComp('a*o.bmp', 'auto.bmp');
    Result := Result and (not WildComp('a*tu.bmp', 'auto.bmp'));
    Result := Result and WildComp('a*o.b*p', 'auto.bmp') and (WildComp('a*to.*', 'auto.bmp'));
    Result := Result and WildComp('a**o.b*p', 'auto.bmp');
    Result := Result and (WildComp('*ut*.**', 'auto.bmp'));
    Result := Result and (WildComp('*ut*.*.*', 'auto.bmp.splack'));
    Result := Result and WildComp('**.**', 'auto.bmp') and (not WildComp('*ut*', 'auto.bmp'));
    // '*' = at least 1 char
    Result := Result and not WildComp('**', 'a');
    // shows '.' <> '*'
    Result := Result and (not WildComp('*ut*.*', 'auto.bmp.foo'));
    // shows un-greedy match
    Result := Result and (WildComp('*ut', 'autout'));

    Result := Result and (not WildComp('auto?', 'auto'));
    Result := Result and not WildComp('?uto', 'uto');
    Result := Result and WildComp('aut?', 'auto');
    Result := Result and WildComp('???', 'uto');
    Result := Result and not WildComp('????', 'uto');
    Result := Result and not WildComp('??', 'uto');

end;
2010-02-19, 23:41:46
MToloo  
Thanks for thomas_kelsey. It's a good and simple method to compare.
It's the above function with a bit change:
it work for all strings, not file names,
it can return the value substituted for wild chars in TStrings. for example:
WidComp('This is * sentence', 'This is a test sentence', WildsValues)
return true and put 'a test' in the WildsValues variable.

function WildComp(const mask: String; const target: String; WildsValues: TStrings= nil): Boolean;
// '*' matches greedy & ungreedy
// simple recursive descent parser - not fast but easy to understand
function WComp(const maskI: Integer; const targetI: Integer; const WildValue: string): Boolean;
begin
if maskI > Length(mask) then begin
Result := targetI = Length(target) + 1;
if Result and (WildValue <> '') and Assigned(WildsValues) and
((mask[maskI - 1] = '*') or (mask[maskI - 1] = '?')) then
WildsValues.Insert(0, WildValue);
Exit;
end;
if targetI > Length(target) then begin
// unread chars in filter or would have read '#0'
Result := False;
Exit;
end;
case mask[maskI] of
'*':
// try with and without ending match - but always matches at least one char
Result :=
WComp(succ(maskI), Succ(targetI), WildValue) or
WComp(maskI, Succ(targetI), WildValue + target[Succ(targetI)]);
'?':
Result := WComp(succ(maskI), Succ(targetI), WildValue)

else // includes '.' which only matches itself
begin
if mask[maskI] = target[targetI] then
Result := WComp(succ(maskI), Succ(targetI), target[Succ(targetI)])
else
Result := False;

if Result and (WildValue <> '') and Assigned(WildsValues) and
((mask[maskI - 1] = '*') or (mask[maskI - 1] = '?')) then
WildsValues.Insert(0, WildValue);
end;
end;// case

end;

begin
if Assigned(WildsValues) then
WildsValues.Clear;
WildComp := WComp(1, 1, target[1]);
end;
2013-02-08, 00:27:06   (updated: 2013-02-08, 00:58:48)
[hidden] from Russian Federation  
{$APPTYPE CONSOLE}
function WildComp(const WildS,IstS: String): boolean;
var
i, j, l, p : Integer;
begin
i := 1;
j := 1;
while (i<=length(WildS)) do
begin
if WildS[i]='*' then
begin
if i = length(WildS) then
begin
result := true;
exit
end
else
begin
{ we need to synchronize }
l := i+1;
while (l < length(WildS)) and (WildS[l+1] <> '*') do
inc (l);
p := pos (copy (WildS, i+1, l-i), IstS);
if p > 0 then
begin
j := p-1;
end
else
begin
result := false;
exit;
end;
end;
end
else
if (WildS[i]<>'?') and ((length(IstS) < i)
        or (WildS[i]<>IstS[j])) then
begin
result := false;
    exit
end;
inc (i);
inc (j);
end;
result := (j > length(IstS));
end;
begin
if WildComp('a*.bmp', 'auto.bmp') then WriteLn('OK 1');
if not WildComp('a*x.bmp', 'auto.bmp') then WriteLn('OK 2');
if WildComp('a*o.bmp', 'auto.bmp') then WriteLn('OK 3');
if not WildComp('a*tu.bmp','auto.bmp') then WriteLn('OK 4');
end.
2013-06-27, 12:59:13
jl from United Kingdom  
Hi 'hidden from russian federation'

Using your wildcomp - works well. Looks as if it works for all strings.

I'd like to know position of the wildcard string in the target string when function is true. Your loops are too clever for me - I've looked at i,j,l & p on exit. Is there an easy way to calculate this?

Regards John

2015-02-20, 22:44:26
anonymous from Indonesia  
Saya haturkan banyak terima kasih, karnah aki Cokeng membantu memberikan solusi,,,kini saya udah lepas dari kendala yg saya alami kemaren, dan angka yg aki berikan sangat tepat, hanya sekali ini saya main togel...Buat kamu yg pengen menang togel....bisa hub: Ki Cokeng di: +6285394330318. terima kasih.
2015-02-20, 22:52:31
anonymous from Indonesia  
Saya haturkan banyak terima kasih, karnah aki Cokeng membantu memberikan solusi,,,kini saya udah lepas dari kendala yg saya alami kemaren, dan angka yg aki berikan sangat tepat, hanya sekali ini saya main togel...Buat kamu yg pengen menang togel....bisa hub: Ki Cokeng di: +6285394330318. terima kasih.
2015-02-21, 21:54:47
anonymous from Indonesia  
Saya haturkan banyak terima kasih, karnah KI COKENG membantu memberikan solusi,,,kini saya udah lepas dari kendala yg saya alami kemaren, dan angka yg aki berikan sangat tepat, hanya sekali ini saya main togel...Buat kamu yg pengen menang togel....bisa hub: Ki Cokeng di: +6285394330318. terima kasih.
2015-02-21, 21:55:22
anonymous from Indonesia  
Saya haturkan banyak terima kasih, karnah KI COKENG membantu memberikan solusi,,,kini saya udah lepas dari kendala yg saya alami kemaren, dan angka yg aki berikan sangat tepat, hanya sekali ini saya main togel...Buat kamu yg pengen menang togel....bisa hub: Ki Cokeng di: +6285394330318. terima kasih.
2015-02-21, 23:06:23
anonymous from Indonesia  
Saya haturkan banyak terima kasih, karnah KI COKENG membantu memberikan solusi,,,kini saya udah lepas dari kendala yg saya alami kemaren, dan angka yg aki berikan sangat tepat, hanya sekali ini saya main togel...Buat kamu yg pengen menang togel....bisa hub: Ki Cokeng di: +6285394330318. terima kasih.
2015-02-25, 19:11:11
anonymous from Indonesia  
Saya haturkan banyak terima kasih, karnah KI COKENG membantu memberikan solusi,,,kini saya udah lepas dari kendala yg saya alami kemaren, dan angka yg aki berikan sangat tepat, hanya sekali ini saya main togel...Buat kamu yg pengen menang togel....bisa hub: Ki Cokeng di: +6285394330318. terima kasih.
2016-01-06, 18:13:01   (updated: 2016-01-06, 18:32:39)
anonymous from Germany  
'[hidden] from Russian Federation' did very well, but if you try:
WildComp('a*u*t*o*.bmp', 'auto.bmp') it will fail
There is a need to add a counter for asterix wildcards to avoid this problem.
Added CaseSensitive selection, too.


function WildComp(const WildS,IstS: String; CaseSensitive: boolean): boolean;
var
    i, j, l, p, a : Integer;
begin
    If not CaseSensitive then
    begin
        WildS := LowerCase(WildS);
        IstS := LowerCase(IstS);
    end;
    i := 1;
    j := 1;
    while (i<=length(WildS)) do
    begin
        if WildS[i] = '*' then
        begin
            inc(a);
            if i = length(WildS) then
            begin
                result := true;
                exit;
            end
            else begin
                { we need to synchronize }
                l := i+1;
                while (l < length(WildS)) and (WildS[l+1] <> '*') do
                begin
                    inc (l);
                end;
                p := pos (copy (WildS, i+1, l-i), IstS);
                if p > 0 then
                begin
                    j := p-1;
                end
                else begin
                    result := false;
                    exit;
                end;
            end;
        end
        else begin
            if (WildS[i]<>'?') and ((length(IstS)+a < i) or (WildS[i]<>IstS[j])) then
            begin
                result := false;
                exit;
            end;
        end;
        inc (i);
        inc (j);
    end;
    result := (j > length(IstS));
end;

begin
  if WildComp('a*.bmp', 'auto.bmp') then WriteLn('OK 1');
  if not WildComp('a*x.bmp', 'auto.bmp') then WriteLn('OK 2');
  if WildComp('a*o.bmp', 'auto.bmp') then WriteLn('OK 3');
  if not WildComp('a*tu.bmp','auto.bmp') then WriteLn('OK 4');
  if WildComp('a*u*t*o*.bmp','auto.bmp') then WriteLn('OK 5');
end.

 

 

NEW: Optional: Register   Login
Email address (not necessary):

Rate as
Hide my email when showing my comment.
Please notify me once a day about new comments on this topic.
Please provide a valid email address if you select this option, or post under a registered account.
 

Show city and country
Show country only
Hide my location
You can mark text as 'quoted' by putting [quote] .. [/quote] around it.
Please type in the code:

Please do not post inappropriate pictures. Inappropriate pictures include pictures of minors and nudity.
The owner of this web site reserves the right to delete such material.

photo Add a picture: