Regular Expressions and TMatch.Groups.Count

delphi, delphi-xe, regex

Solution

Match.Groups[0] contains the whole expression, so this is correct.

TGroupcollection constructor:

constructor TGroupCollection.Create(ARegEx: TPerlRegEx;
  const AValue: UTF8String; AIndex, ALength: Integer; ASuccess: Boolean);
var
  I: Integer;
begin
  FRegEx := ARegEx;
  /// populate collection;
  if ASuccess then
  begin
    SetLength(FList, FRegEx.GroupCount + 1);
    for I := 0 to Length(FList) - 1 do
      FList[I] := TGroup.Create(AValue, FRegEx.GroupOffsets[I], FRegEx.GroupLengths[I], ASuccess);
  end;
end;

As you can see the internal Flist (`TArray<TGroup>`) is initiated with the number of groups + 1. FList[0] receives a group with offset 1 and the whole expression length. This behaviour is not documented.

Problem

I'm porting some classes from the Apache Commons library, and I found the following behaviour strange. I have a regular expression defined as ``` const IPV4_REGEX = '^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$'; ``` and I use it as follows: ``` ipv4Validator: TRegEx; ipv4Validator := TRegEx.Create(IPV4_REGEX); ``` When I use it to match an IP address, the following code returns false - the debugger shows that `Match.Groups.Count` is 5, which I didn't expect. ``` var Match: TMatch; begin Match := ipv4Validator.Match(inet4Address); if Match.Groups.Count <> 4 then Exit(false); ``` Is this the correct behaviour of TMatch.Groups.Count? Just in case, here's the full code of my class. Notice that I have commented the offending line, because it made my tests fail. ``` unit InetAddressValidator; interface uses RegularExpressions; const IPV4_REGEX = '^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$'; type TInetAddressValidator = class private ipv4Validator: TRegEx; public constructor Create; overload; function isValid(const inetAddress: String): Boolean; function isValidInet4Address(const inet4Address: String): Boolean; end; implementation uses SysUtils; constructor TInetAddressValidator.Create; begin inherited; ipv4Validator := TRegEx.Create(IPV4_REGEX); end; function TInetAddressValidator.isValid(const inetAddress: String): Boolean; begin Result := isValidInet4Address(inetAddress); end; function TInetAddressValidator.isValidInet4Address(const inet4Address : String): Boolean; var Match: TMatch; IpSegment: Integer; i: Integer; begin Match := ipv4Validator.Match(inet4Address); // if Match.Groups.Count <> 4 then // Exit(false); IpSegment := 0; for i := 1 to Match.Groups.Count - 1 do begin try IpSegment := StrToInt(Match.Groups[i].Value); except Exit(false); end; if IpSegment > 255 then Exit(false); end; Result := true; end; end. ```

Original source