Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a regular expression in Delphi using TRegEx

Tags:

regex

delphi

I am looking to create a regular expression in Delphi XE that will match a number followed by one decimal point, followed by (essentially) an unlimited number of digits.

Valid examples:

2.334
150.2
0.23
3

Invalid examples:

3..42
4-2.3
e5.64
3 145

The decimal point may be optional and integers are also okay.

How would one go about doing this in Delphi using TRegEx?

Edit:

This is what I have thus far:

enter function CheckCoefficientBoxesValidInput(InputtedTerm : TEdit) : boolean;

 var
  RegularExpression : TRegEx;
  Match : TMatch;

 begin
  RegularExpression.Create('[-+]?[0-9]*\.?[0-9]+');
  Match := RegularExpression.Match(InputtedTerm.Text);
  if Match.Success then
   begin
    ShowMessage('Success.');
   end;

 end;

Edit 2:

Trying @DavidHeffernan's code:

Function CheckCoefficientBoxesValidInput(InputtedTerm : TEdit) : boolean;
  var
    RegularExpression : TRegEx;
    Match : TMatch;
  begin
    CheckCoefficientBoxesValidInput := true;
    if not RegularExpression.IsMatch(InputtedTerm.Text, '[-+]?[0-9]*\.?[0-9]+') then
      CheckCoefficientBoxesValidInput := false;
  end;

Unfortunately this doesn't seem to be working.

like image 263
Gigabit Avatar asked Dec 17 '14 21:12

Gigabit


2 Answers

You probably want to cater for a sign too, to allow for negative numbers. This should do.

[-+]?[0-9]*\.?[0-9]+

This won't recognise scientific notation but then you did not ask for that. This returns True if the pattern can be found anywhere inside the input text. I don't know your full requirements, but I guess you want to match against the entire input string. Use the ^ and $ start and end of line anchors for that. And perhaps you want to allow whitespace around the value too:

^\s*[-+]?[0-9]*\.?[0-9]+\s*$

Test for a match like this:

TRegEx.IsMatch(Input, '^\s*[-+]?[0-9]*\.?[0-9]+\s*$')

A demonstration:

{$APPTYPE CONSOLE}

uses
  System.RegularExpressions;

procedure Check(const Input: string);
begin
  Writeln(Input, ' ', TRegEx.IsMatch(Input, '^\s*[-+]?[0-9]*\.?[0-9]+\s*$'));
end;

begin
  Check('2.334');
  Check('150.2');
  Check('0.23');
  Check('3');
  Check('3..42');
  Check('4-2.3');
  Check('e5.64');
  Check('3 145');
end.

Output

2.334 TRUE
150.2 TRUE
0.23 TRUE
3 TRUE
3..42 FALSE
4-2.3 FALSE
e5.64 FALSE
3 145 FALSE

The reference for Delphi regular expressions is here: http://www.regular-expressions.info/

And the documentation for the Delphi regular expression class: http://docwiki.embarcadero.com/Libraries/en/System.RegularExpressions.TRegEx


It would be far easier just to call TryStrToFloat.

like image 123
David Heffernan Avatar answered Oct 23 '22 12:10

David Heffernan


I know all the cool kids love RegEx, but imho it is overkill for this sort of problem when a very simple validation routine will do the job (and is far more likely to be comprehensible in months/years to come).

Something along the lines of:

function IsValidNumber(s: String): Boolean;
var
  parts: TStringDynArray;
begin
  parts := SplitString(s, '.');
  case Length(parts) of
    1: result := StrToIntDef(s, -1) <> -1;
    2: result :=      (StrToIntDef(parts[0], -1) <> -1)
                 and  (StrToIntDef(parts[1], -1) <> -1);
  else
    result := FALSE;
  end;
end;

Having said that, a more general solution may be desirable and as an aside I would mention that in my own code if I had the same requirement I would use my own string template class.

This is essentially a "regex for mere mortals", allowing a simpler process of pattern matching based on variable elements within a string - optionally of an enforced type (int, guid etc) - separated by well-known literal parts.

In this case my class would have solved the problem thus:

 if TStringTemplate.Match(['[:int]', '[:int].[:int]'], s) then

You supply an array of templates and a candidate string to match against those templates. The first match 'wins' and TRUE is returned. If there are no matches then FALSE is returned. Where a string matches a template you can optionally capture the variable parts into a name/value pair list (a TStringList which you must supply). So, given these calls:

 TStringTemplate.Match(['[whole:int]', '[whole:int].[decimal:int]'], '42', list);
 TStringTemplate.Match(['[whole:int]', '[whole:int].[decimal:int]'], '3.14159', list);

Then list will contain, in each case:

[0] whole=42

and

[0] whole=3
[1] decimal=14159

If the variable parts aren't named then the corresponding variables are simply added to the list as values in the order in which they occur in the matched value:

 TStringTemplate.Match(['[whole:int]', '[:int].[:int]'], '3.14159', list);

Will yield the list:

[0] 3
[1] 14159

Unfortunately I cannot currently publish this class as it is in turn dependent upon my own string library which is currently undergoing a major overhaul, prior to release in my github repo. But if you are interested I will let you know when it's ready (v. soon) or you can just "watch" my repo.

like image 22
Deltics Avatar answered Oct 23 '22 11:10

Deltics