Delphi XE中的正则表达式命名捕获组

8

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

我的真实代码有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",正如我所预期的那样。是否有我不能使用的保留字?我认为没有,因为在我的实际示例中,我尝试了完全随机的名称。我只是无法弄清楚是什么导致了这种行为。


你应该在质量控制报告中报告这个问题,因为显然这是一个bug。 - jachguate
2个回答

7

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

PCRE_ERROR_NOSUBSTRING 在 RegularExpressionsAPI 中定义为 PCRE_ERROR_NOSUBSTRING = -7

一些测试显示,pcre_get_stringnumber 对于每个名称的第一个字母在 kz 范围内的名称都会返回 PCRE_ERROR_NOSUBSTRING,并且该范围取决于 judge 中的第一个字母。更改 judge 会更改范围。

我认为这里至少涉及两个错误。一个是 pcre_get_stringnumber 中的错误,另一个是 TGroupCollection.GetItem 需要引发适当的异常而不是 SRegExIndexOutOfBounds


非常感谢您深入挖掘这个问题。我已经找到了一个解决方法,使用RegexBuddy作者的TPerlRegEx库。据我所知,这个库是XE实现的基础,所以我觉得差异非常神秘。 - Stefan Fjellsten

5
这个bug似乎出现在RegularExpressionsAPI单元中,它包装了PCRE库,或者是它链接的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组件添加pcre单元,则会正确地打印1和2。Delphi XE中的RegularExpressionsAPI基于我的pcre单元,而RegularExpressionsCore单元基于我的PerlRegEx单元。Embarcadero对这两个单元进行了一些更改。他们还从PCRE库编译了自己的OBJ文件,这些文件由RegularExpressionsAPI链接。我已将此错误报告为QC 92497
我还创建了一个单独的报告QC 92498,请求TGroupCollection.GetItem在请求不存在的命名组时引发更合理的异常。(此代码位于基于Vincent Parrett编写的代码的RegularExpressions单元中,而非我的代码。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接