Regex在Delphi XE中命名了捕获组

时间:2011-03-16 08:45:01

标签: regex delphi delphi-xe regexbuddy

我在RegexBuddy中构建了一个匹配模式,其行为完全符合我的预期。但我无法将此转移到Delphi XE,至少在使用最新的内置TRegEx或TPerlRegEx时。

我的真实世界代码有6个捕获组,但我可以在一个更简单的示例中说明问题。此代码在第一个对话框中显示“3”,然后在执行第二个对话框时引发异常(-7索引超出范围)。

var
  Regex: TRegEx;
  M: TMatch;
begin
  Regex := TRegEx.Create('(?P<time>\d{1,2}:\d{1,2})(?P<judge>.{1,3})');
  M := Regex.Match('00:00  X1 90  55KENNY BENNY');
  ShowMessage(IntToStr(M.Groups.Count));
  ShowMessage(M.Groups['time'].Value);
end;

但如果我只使用一个捕获组

Regex := TRegEx.Create('(?P<time>\d{1,2}:\d{1,2})');

第一个对话框显示“2”,第二个对话框将按预期显示时间“00:00”。

然而,如果只允许一个命名的捕获组,这将有点限制,但事实并非如此......如果我将捕获组名称更改为例如“atime”。

var
  Regex: TRegEx;
  M: TMatch;
begin
  Regex := TRegEx.Create('(?P<atime>\d{1,2}:\d{1,2})(?P<judge>.{1,3})');
  M := Regex.Match('00:00  X1 90  55KENNY BENNY');
  ShowMessage(IntToStr(M.Groups.Count));
  ShowMessage(M.Groups['atime'].Value);
end;

我会得到“3”和“00:00”,正如预期的那样。有保留的话我不能用吗?我不这么认为,因为在我的真实例子中,我尝试过完全随机的名字。我无法弄清楚导致这种行为的原因。

2 个答案:

答案 0 :(得分:7)

pcre_get_stringnumber找不到名称时,会返回PCRE_ERROR_NOSUBSTRING

PCRE_ERROR_NOSUBSTRING在RegularExpressionsAPI中定义为PCRE_ERROR_NOSUBSTRING = -7

某些测试表明,pcre_get_stringnumber会为PCRE_ERROR_NOSUBSTRINGk范围内第一个字母的每个名称返回z,并且该范围取决于第一个字母在judge。将judge更改为其他内容会更改范围。

正如我所看到的那样,至少有两个漏洞。一个在pcre_get_stringnumber中,一个在TGroupCollection.GetItem中,需要引发一个正确的异常,而不是SRegExIndexOutOfBounds

答案 1 :(得分:5)

该错误似乎位于包含PCRE库的RegularExpressionsAPI单元中,或者在它链接的PCRE OBJ文件中。如果我运行此代码:

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils, RegularExpressionsAPI;

var
  myregexp: Pointer;
  Error: PAnsiChar;
  ErrorOffset: Integer;
  Offsets: array[0..300] of Integer;
  OffsetCount, Group: Integer;

begin
  try
    myregexp := pcre_compile('(?P<time>\d{1,2}:\d{1,2})(?P<judge>.{1,3})', 0, @error, @erroroffset, nil);
    if (myregexp <> nil) then begin
      offsetcount := pcre_exec(myregexp, nil, '00:00  X1 90  55KENNY BENNY', Length('00:00  X1 90  55KENNY BENNY'), 0, 0, @offsets[0], High(Offsets));
      if (offsetcount > 0) then begin
        Group := pcre_get_stringnumber(myregexp, 'time');
        WriteLn(Group);
        Group := pcre_get_stringnumber(myregexp, 'judge');
        WriteLn(Group);
      end;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  ReadLn;
end.

它打印-7和2而不是1和2。

如果我从uses子句中删除RegularExpressionsAPI并从我的TPerlRegEx component添加pcre单元,则它会正确打印1和2.

Delphi XE中的RegularExpressionsAPI基于我的pcre单元,RegularExpressionsCore单元基于我的PerlRegEx单元。 Embarcadero确实对这两个单位做了一些改变。他们还从PCRE库中编译了自己的OBJ文件,这些文件由RegularExpressionsAPI链接。

我已将此错误报告为QC 92497

我还创建了一个单独的报告QC 92498,以请求TGroupCollection.GetItem在请求不存在的命名组时引发更明智的异常。 (此代码位于RegularExpressions单元中,该单元基于Vincent Parrett编写的代码,而非我自己。