我写了一个正则表达式,其工作是将所有匹配返回到其三个备选捕获组.我的目标是学习哪个捕获组生成每个匹配. PCRE似乎能够生成这些信息.但是我还没有在德尔福XE8中强制使用TRegEx类来为匹配产生有意义的捕获组信息.我不能声称是正则表达式的领导,而TRegEx对我来说是新的,所以谁知道我在做什么错误.
正则表达式(regex101.com workpad)是:
(?'word'\b[a-zA-Z]{3,}\b)|(?'id'\b\d{1,3}\b)|(?'course'\b[BL]\d{3}\b)
这个测试文本:
externship L763 clinic 207 B706 b512
在测试环境中给出五个匹配.但是,在TMatchCollection中移动每个TMatch的TGroupCollection的简单测试程序会显示关于组的奇怪结果:所有匹配都有多个组(2,3或4),每个组的“成功”为true,并且经常匹配的文本在几个组或是空的.所以这个数据结构(下面)不是我期望的:
Using TRegEx Regex: (?'word'\b[a-zA-Z]{3,3}\b)|(?'course'\b[BL]\d{3}\b) Text: externship L763 clinic 207 B706 b512 5 matches 'externship' with 2 groups: length 10 at 1 value 'externship' (Sucess? True) length 10 at 1 value 'externship' (Sucess? True) 'L763' with 4 groups: length 4 at 12 value 'L763' (Sucess? True) length 0 at 1 value '' (Sucess? True) length 0 at 1 value '' (Sucess? True) length 4 at 12 value 'L763' (Sucess? True) 'clinic' with 2 groups: length 6 at 17 value 'clinic' (Sucess? True) length 6 at 17 value 'clinic' (Sucess? True) '207' with 3 groups: length 3 at 24 value '207' (Sucess? True) length 0 at 1 value '' (Sucess? True) length 3 at 24 value '207' (Sucess? True) 'B706' with 4 groups: length 4 at 28 value 'B706' (Sucess? True) length 0 at 1 value '' (Sucess? True) length 0 at 1 value '' (Sucess? True) length 4 at 28 value 'B706' (Sucess? True)
我的简单测试运行是这样的:
program regex_tester; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils,System.RegularExpressions,System.RegularExpressionscore; var Matched : Boolean; J : integer; Group : TGroup; Match : TMatch; Matches : TMatchCollection; RegexText,TestText : String; RX : TRegEx; RXPerl : TPerlRegEx; begin try RegexText:='(?''word''\b[a-zA-Z]{3,}\b)|(?''id''\b\d{1,3}\b)|(?''course''\b[BL]\d{3}\b)'; TestText:='externship L763 clinic 207 B706 b512'; RX:=TRegex.Create(RegexText); Matches:=RX.Matches(TestText); Writeln(Format(#10#13#10#13'Using TRegEx'#10#13'Regex: %s'#10#13'Text: %s'#10#13,[RegexText,TestText])); Writeln(Format('%d matches',[Matches.Count])); for Match in Matches do begin Writeln(Format(' ''%s'' with %d groups:',[Match.Value,Match.Groups.Count])); for Group in Match.Groups do Writeln(Format(#9'length %d at %d value ''%s'' (Sucess? %s)',[Group.Length,Group.Index,Group.Value,BoolToStr(Group.Success,True)])); end; RXPerl:=TPerlRegEx.Create; RXPerl.Subject:=TestText; RXPerl.RegEx:=RegexText; Writeln(Format(#10#13#10#13'Using TPerlRegEx'#10#13'Regex: %s'#10#13'Text: %s'#10#13,[RXPerl.Regex,RXPerl.Subject])); Matched:=RXPerl.Match; if Matched then repeat begin Writeln(Format(' ''%s'' with %d groups:',[RXPerl.MatchedText,RXPerl.GroupCount])); for J:=1 to RXPerl.GroupCount do Writeln(Format(#9'length %d at %d,value ''%s''',[RXPerl.GroupLengths[J],RXPerl.GroupOffsets[J],RXPerl.Groups[J]])); Matched:=RXPerl.MatchAgain; end; until Matched=false; except on E: Exception do Writeln(E.ClassName,': ',E.Message); end; end.
我一定会赞赏向正确的方向微调.如果TRegEx坏了,我当然可以使用一种替代方法 – 或者我可以放弃感觉到的优雅的解决方案,而是使用三个更简单的测试来找到我需要的信息.
增加了信息和解释
正如@ andrei-galatyn所说,TRegEx使用TPerlRegEx进行工作.所以我添加了一个部分到我的测试程序(输出如下),我也在尝试.使用TRegEx并不方便,但其结果应该是 – 而且没有TRegEx破碎的TGroup数据结构的问题.无论使用哪一类,最后一组索引(TRegEx少于1)是我想要的捕获组.
一路上,我被提醒说,Pascal数组通常基于1而不是0.
Using TPerlRegEx Regex: (?'word'\b[a-zA-Z]{3,3}\b)|(?'course'\b[BL]\d{3}\b) Text: externship L763 clinic 207 B706 b512 'externship' with 1 groups: length 10 at 1,value 'externship' 'L763' with 3 groups: length 0 at 1,value '' length 0 at 1,value '' length 4 at 12,value 'L763' 'clinic' with 1 groups: length 6 at 17,value 'clinic' '207' with 2 groups: length 0 at 1,value '' length 3 at 24,value '207' 'B706' with 3 groups: length 0 at 1,value '' length 4 at 28,value 'B706'
解决方法
Delphi内部使用TPerlRegEx类,它对GroupCount属性有这样的描述:
存储在“组”数组中的匹配组数.该数字是您正式表达式中实际参与最后一场比赛的最高数量的捕获组的数量.它可能小于正则表达式中捕获组的数量.
例如.当正则表达式“(a)|(b)”匹配“a”时,GroupCount将为1.当相同的正则表达式匹配“b”时,GroupCount将为2.
TRegEx类总是增加一个组(对于整个表达式我猜).
在你的情况下,应该足以检查每一个这样的比赛:
case Match.Groups.Count-1 of 1: ; // "word" found 2: ; // "id" found 3: ; // "course" found end;
这并不能解释为什么群组中充斥着奇怪的数据,实际上似乎足以回答你的问题.