Recent

Author Topic: LIKE operator for Pascal  (Read 15524 times)

Bazzao

  • Full Member
  • ***
  • Posts: 178
  • Pies are squared.
LIKE operator for Pascal
« on: September 14, 2017, 03:43:23 am »
Has anyone developed, know of a source or, a LIKE function for Pascal similar to VBA's LIKE Operator?

LIKE operator reference:
https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/operators/like-operator

The Pattern Options I am particularly interested in are (apart from the usual wilds * and ?), are
# Any single digit (0–9)
[charlist]   Any single character in charlist
[!charlist]   Any single character not in charlist

I have looked at the IsWild function, and as it stands, it is not suitable.

TIA

Bazza
Bazza

Lazarus 2.0.10; FPC 3.2.0; SVN Revision 63526; x86_64-win64-win32/win64
Windows 10.

Edson

  • Hero Member
  • *****
  • Posts: 1301
Re: LIKE operator for Pascal
« Reply #1 on: September 14, 2017, 03:59:45 am »
I use this function:

Code: Pascal  [Select][+][-]
  1. function StringLike(const str: string; mask: string): boolean;
  2. {Utilidad para comparación de cadenas al estilo de VB. El patrón de comparación es
  3. "mask" y tiene los siguientes comodines:
  4. '?' -> coincide con cualquier caracter.
  5. '*' -> coincide con cualquier texto.
  6. '#' -> coincide con cualquier caracter numércio.
  7. '[]' -> indica un conjunto de cacacteres.
  8. }
  9. var
  10.   msk: TMask;
  11. begin
  12.   mask := StringReplace(mask, '#', '[0-9]', [rfReplaceAll]);
  13.   msk := Tmask.Create(mask);
  14.   Result := msk.Matches(str);
  15.   msk.Destroy;
  16. end;
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

Bazzao

  • Full Member
  • ***
  • Posts: 178
  • Pies are squared.
Re: LIKE operator for Pascal
« Reply #2 on: September 14, 2017, 04:06:57 am »
Running that thru Google translator makes it look even better (looked good in Spanish).

I'm going to give that a go.

Thanks,

B
Bazza

Lazarus 2.0.10; FPC 3.2.0; SVN Revision 63526; x86_64-win64-win32/win64
Windows 10.

Bazzao

  • Full Member
  • ***
  • Posts: 178
  • Pies are squared.
Re: LIKE operator for Pascal
« Reply #3 on: September 14, 2017, 04:42:02 am »

Trialling out the function, i am running a few tests, looking at the same string (random co-ordinate in Degrees Minutes Seconds), with different masks. All should be true, satisfying the proper calls..

Output shows:
Quote
N 10 32 29 W 47 4 21>> [NSEW]?[0-9]*[NSEW]?[0-9]*= FALSE
N 10 32 29 W 47 4 21>> [NSEW]?[0-9]*= FALSE
N 10 32 29 W 47 4 21>> [NSEW]*[0-9]*= FALSE
N 10 32 29 W 47 4 21>> [N,S,E,W]?[0-9]*[N,S,E,W]?[0-9]*= FALSE
N 10 32 29 W 47 4 21>> [N,S,E,W]?[0-9]*= FALSE
N 10 32 29 W 47 4 21>> [N,S,E,W]*[0-9]*= FALSE

The objective is to test for
Hemisphere (N/S/E/W) then digit then Hemisphere (N/S/E/W) then digit. This would confirm a possible full co-ordinate.

Code: Pascal  [Select][+][-]
  1. program StringLike1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  7.   cthreads,
  8.   {$ENDIF}{$ENDIF}
  9.   Classes, Masks, SysUtils, crt
  10.   { you can add units after this };
  11.  
  12. {$R *.res}
  13.  
  14. var
  15.   ch:char;
  16.  
  17. function StringLike(const str: string; mask: string): boolean;
  18. {Utility for VB style string comparison. The pattern of comparison is
  19. "mask" and has the following wildcards:
  20. '?' -> matches any character.
  21. '*' -> matches any text.
  22. '#' -> matches any numeric character.
  23. '[]' -> indicates a set of cacacters.
  24. }
  25. var
  26.   msk: TMask;
  27. begin
  28.   mask := StringReplace(mask, '#', '[0-9]', [rfReplaceAll]);
  29.   try
  30.     msk := Tmask.Create(mask);
  31.   except
  32.     on E: Exception do
  33.       writeln('ERROR 1: ',E.Message);
  34.   end;
  35.   try
  36.     Result := msk.Matches(str);
  37.   except
  38.     on E: Exception do
  39.       writeln('ERROR 2: ',E.Message);
  40.   end;
  41.   try
  42.     msk.Destroy;
  43.   except
  44.     on E: Exception do
  45.       writeln('ERROR 3: ',E.Message);
  46.   end;
  47. end;
  48.  
  49. procedure Test0(pStr,pMsk:string);
  50. begin
  51.   writeln(pStr,' ',pMsk,' ',StringLike(pStr,pMsk));
  52. end;
  53.  
  54. procedure Test1;
  55. begin
  56.   Test0('N 10 32 29 W 47 4 21','[N,S,E,W]?[0-9]*[N,S,E,W]?[0-9]*');
  57.   Test0('N 10 32 29 W 47 4 21','[N,S,E,W]?[0-9]*');
  58.   ch:=readkey;
  59.   if ch=#27 then writeln;
  60. end;
  61.  
  62. begin
  63.   Test1;
  64. end.
  65.  

B

Bazza

Lazarus 2.0.10; FPC 3.2.0; SVN Revision 63526; x86_64-win64-win32/win64
Windows 10.

bytebites

  • Hero Member
  • *****
  • Posts: 633
Re: LIKE operator for Pascal
« Reply #4 on: September 14, 2017, 05:13:01 am »
Code: Pascal  [Select][+][-]
  1.  msk := Tmask.Create(mask,true);

Change the mask to case sensitive.

Bazzao

  • Full Member
  • ***
  • Posts: 178
  • Pies are squared.
Re: LIKE operator for Pascal
« Reply #5 on: September 14, 2017, 05:27:01 am »
Well that's a result.

Thanks bytebites.

B
Bazza

Lazarus 2.0.10; FPC 3.2.0; SVN Revision 63526; x86_64-win64-win32/win64
Windows 10.

Akira1364

  • Hero Member
  • *****
  • Posts: 561
Re: LIKE operator for Pascal
« Reply #6 on: September 14, 2017, 05:40:03 am »
I use this function:

Code: Pascal  [Select][+][-]
  1. function StringLike(const str: string; mask: string): boolean;
  2. {Utilidad para comparación de cadenas al estilo de VB. El patrón de comparación es
  3. "mask" y tiene los siguientes comodines:
  4. '?' -> coincide con cualquier caracter.
  5. '*' -> coincide con cualquier texto.
  6. '#' -> coincide con cualquier caracter numércio.
  7. '[]' -> indica un conjunto de cacacteres.
  8. }
  9. var
  10.   msk: TMask;
  11. begin
  12.   mask := StringReplace(mask, '#', '[0-9]', [rfReplaceAll]);
  13.   msk := Tmask.Create(mask);
  14.   Result := msk.Matches(str);
  15.   msk.Destroy;
  16. end;

↓↓↓ MFW you call Destroy directly and not Free ↓↓↓
« Last Edit: September 14, 2017, 05:44:04 am by Akira1364 »

Bazzao

  • Full Member
  • ***
  • Posts: 178
  • Pies are squared.
Re: LIKE operator for Pascal
« Reply #7 on: September 14, 2017, 05:52:12 am »
Noted Akira1364.

The next issue is some DMS indicate hemisphere with a negative (S or W)

If I add

  Test0('-123 45.678','[-#]## #*');

I get an execution error.

Similarly with

  Test0('-123 45.678','[-0123456789]## #*');

How do I test for hyphen along with digits (remember first may be positive or negative)?

The only way I can see is to test via character independently.

I am getting only a few results searching for pascal TMask.

B

« Last Edit: September 14, 2017, 10:23:00 am by Bazzao »
Bazza

Lazarus 2.0.10; FPC 3.2.0; SVN Revision 63526; x86_64-win64-win32/win64
Windows 10.

Bazzao

  • Full Member
  • ***
  • Posts: 178
  • Pies are squared.
Re: LIKE operator for Pascal
« Reply #8 on: September 14, 2017, 10:25:45 am »
Also, another question ...

Instead of creating and freeing every match, can I create upon program initialization and free upon termination. A look at the popup list for TMask did not reveal anything I am sure of.

B
Bazza

Lazarus 2.0.10; FPC 3.2.0; SVN Revision 63526; x86_64-win64-win32/win64
Windows 10.

bytebites

  • Hero Member
  • *****
  • Posts: 633
Re: LIKE operator for Pascal
« Reply #9 on: September 14, 2017, 02:17:31 pm »
I upload modified version of masks-file.
No need to use string replace function.
Create tmask-instance for each mask-string.

Code: Pascal  [Select][+][-]
  1. uses masks2;
  2.  
  3. var
  4.   msk: TMask;
  5. begin  
  6.   msk := Tmask.Create('-123 45.678');
  7.   Result := msk.Matches('{/-}### ##.###');
  8.   msk.free;
  9. end.
  10.  
« Last Edit: September 14, 2017, 02:22:44 pm by bytebites »

Thaddy

  • Hero Member
  • *****
  • Posts: 14204
  • Probably until I exterminate Putin.
Re: LIKE operator for Pascal
« Reply #10 on: September 14, 2017, 02:45:48 pm »
I fiddled this together, which is like, like VB syntax.
- Uses some code from swiss delphi center
- Uses macro for like
- Uses (or mis-uses) operator overload ><
Then you get this ;) :
Code: Pascal  [Select][+][-]
  1. program test;
  2. {$mode objfpc}{$H+}
  3. {$macro on}{$define like:=><}
  4. uses sysutils, strutils;
  5.  
  6. operator ><(const source,pattern:string):boolean;overload;
  7. var
  8. // source is from matchstrings function swissdelphicenter
  9.   pSource: array [0..255] of Char;
  10.   pPattern: array [0..255] of Char;
  11.  
  12.   function MatchPattern(element, pattern: PChar): Boolean;
  13.  
  14.     function IsPatternWild(pattern: PChar): Boolean;    
  15.     begin
  16.       Result := StrScan(pattern, '*') <> nil;
  17.       if not Result then Result := StrScan(pattern, '?') <> nil;
  18.     end;
  19.   begin
  20.     if 0 = StrComp(pattern, '*') then
  21.       Result := True
  22.     else if (element^ = Chr(0)) and (pattern^ <> Chr(0)) then
  23.       Result := False
  24.     else if element^ = Chr(0) then
  25.       Result := True
  26.     else
  27.     begin
  28.       case pattern^ of
  29.         '*': if MatchPattern(element, @pattern[1]) then
  30.             Result := True
  31.           else
  32.             Result := MatchPattern(@element[1], pattern);
  33.           '?': Result := MatchPattern(@element[1], @pattern[1]);
  34.         else
  35.           if element^ = pattern^ then
  36.             Result := MatchPattern(@element[1], @pattern[1])
  37.           else
  38.             Result := False;
  39.       end;
  40.     end;
  41.   end;
  42. begin
  43.   StrPCopy(pSource, Source);
  44.   StrPCopy(pPattern, pattern);
  45.   Result := MatchPattern(pSource, pPattern);
  46. end;
  47.  
  48. begin
  49.   if 'Sean Stamley' like 'Sean*' then writeln('Match'); // there you go! like in FreePascal
  50. end.
  51.  

Code works... Don't take it seriously, though. It is more like "just because I can" code.. ::) :D
Alternatively you can overload the in operator with the same code or use >< like I wrote it. The macro is just for the keyword like...

(original sdc code from here http://www.swissdelphicenter.ch/en/showcode.php?id=307 needs some touches, though)

Note I overloaded "symmetric difference" ><, because "contains" (<=) is already overloaded for strings and in will be soon (partially done in trunk). see:
Code: Pascal  [Select][+][-]
  1. var
  2.   s:array[0..1] of string = ('Sean','Bean');
  3. begin
  4.   if 'Sean' <= 'ea' then writeln('Match');
  5.   if 'Sean' in  s then writeln('Match');
  6. end.
« Last Edit: September 14, 2017, 03:19:05 pm by Thaddy »
Specialize a type, not a var.

Thaddy

  • Hero Member
  • *****
  • Posts: 14204
  • Probably until I exterminate Putin.
Re: LIKE operator for Pascal
« Reply #11 on: September 14, 2017, 05:25:55 pm »
Ok. rethink and proof of concept. Why not have code like this?:
Code: Pascal  [Select][+][-]
  1. var s:Ansistring  =  'Sean Boon';
  2. begin
  3.   writeln('Sean Bean'.Like('S*an?B*'));
  4.   writeln(S.Like('SA*an?B*'));
  5. end.

All we need is a little type helper, like this:
Code: Pascal  [Select][+][-]
  1. type
  2.   TLikeHelper = type helper for AnsiString
  3.   function Like(const Pattern:AnsiString):Boolean;
  4.   end;

Now that's quite neat, isn't it?
With the help of the (not really nice) code from the above swiss delphi center example that becomes something like:
Code: Pascal  [Select][+][-]
  1. program test2;
  2. {$mode objfpc}{$H+}{$modeswitch typehelpers}
  3. uses sysutils;
  4. type
  5.   TLikeHelper = type helper for AnsiString
  6.   function Like(const Pattern:AnsiString):Boolean;
  7.   end;
  8.   function TLikeHelper.Like(const Pattern:AnsiString):Boolean;
  9.  
  10.   function MatchPattern(element, pattern: PChar): Boolean;
  11.  
  12.     function IsPatternWild(pattern: PChar): Boolean;    
  13.     begin
  14.       Result := StrScan(pattern, '*') <> nil;
  15.       if not Result then Result := StrScan(pattern, '?') <> nil;
  16.     end;
  17.   begin
  18.     if 0 = StrComp(pattern, '*') then
  19.       Result := True
  20.     else if (element^ = Chr(0)) and (pattern^ <> Chr(0)) then
  21.       Result := False
  22.     else if element^ = Chr(0) then
  23.       Result := True
  24.     else
  25.     begin
  26.       case pattern^ of
  27.         '*': if MatchPattern(element, @pattern[1]) then
  28.             Result := True
  29.           else
  30.             Result := MatchPattern(@element[1], pattern);
  31.           '?': Result := MatchPattern(@element[1], @pattern[1]);
  32.         else
  33.           if element^ = pattern^ then
  34.             Result := MatchPattern(@element[1], @pattern[1])
  35.           else
  36.             Result := False;
  37.       end;
  38.     end;
  39.   end;
  40. var
  41. // source is adapted from matchstrings function swissdelphicenter
  42.   pSource, pPattern: PAnsiChar;
  43. begin
  44.   pSource :=AllocMem(Length(Self));
  45.   pPattern := AllocMem(Length(Pattern));
  46.   StrPCopy(pSource, Self);
  47.   StrPCopy(pPattern, pattern);
  48.   Result := MatchPattern(pSource, pPattern);
  49.   Freemem(pSource);
  50.   FreeMem(pPattern);
  51. end;
  52.  
  53.  
  54. begin
  55.  writeln('Sean Bean'.Like('SA*an?B*'));
  56. end.

Now all we have to do is improve on the code (I did, but even more). but the principle is ok and can be added to sysutils.TStringhelper when finished.
Note you could replace the internal code with the code from Edson... much better... but that can not be added to sysutils because it depends on masks.
I'll see if I can factor out the masks code, or add this helper to masks (or even rexexpr)



« Last Edit: September 14, 2017, 06:15:49 pm by Thaddy »
Specialize a type, not a var.

sam707

  • Guest
Re: LIKE operator for Pascal
« Reply #12 on: September 15, 2017, 01:59:29 am »
{$mode smartass ON}
I guess that both regex and regexpr FPK units would already do the job in a way, standartized for decades.

I did not use Qt QValidadator for long, but if I remember, it has a 3-states result possibility, where boolean is not enough, LIKE (matched,unmached, partial), plus a possible callback on expressions failures (partials), called with indices

so... infinite solutions are available by using what exists in regular expressions Standard, and coding a little bit, depending wanted flavor.

Knowing that, the need to become part of sysutils is then very relative  :D

or maybe its my... how you said? lack of knowledge HAHAHAHAHAH  >:D

I love grumpies but I still prefer shrimpies  :P
{$mode smartass OFF}
« Last Edit: September 15, 2017, 03:09:39 am by sam707 »

Thaddy

  • Hero Member
  • *****
  • Posts: 14204
  • Probably until I exterminate Putin.
Re: LIKE operator for Pascal
« Reply #13 on: September 15, 2017, 08:10:10 am »
You can use RexExpr as the underlying engine for the operator or the typehelper ..... As I wrote. It is more syntax.
Specialize a type, not a var.

Bazzao

  • Full Member
  • ***
  • Posts: 178
  • Pies are squared.
Re: LIKE operator for Pascal
« Reply #14 on: September 16, 2017, 03:22:29 am »
I upload modified version of masks-file.
No need to use string replace function.
Create tmask-instance for each mask-string.

Thanks bytebites,

I tried the usual \- and other normal ways of quoting a reserved character..

Yet to give your input a go, but looks like it will work.

Hopefully I can find file uploaded.

Bazza
Bazza

Lazarus 2.0.10; FPC 3.2.0; SVN Revision 63526; x86_64-win64-win32/win64
Windows 10.

 

TinyPortal © 2005-2018