Я написал регулярное выражение, задачей которого является возвращение всех совпадений к трем альтернативным группам захвата. Моя цель - узнать, какая группа захвата произвела каждый матч. PCRE, похоже, может предоставить эту информацию. Но я еще не смог принудить класс TRegEx
в Delphi XE8 получить значимую информацию группы захвата для совпадений. Я не могу утверждать, что возглавляю класс регулярных выражений, а TRegEx
для меня новичок, поэтому кто знает, какие ошибки я делаю.
Регулярное выражение (команда regex101.com):
(?'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
дает пять совпадений в тестовых средах. Но простая тестовая программа, которая проходит TGroupCollection
каждого TMatch
в TMatchCollection
, показывает странные результаты о группах: все совпадения имеют более одной группы (2, 3 или 4) с каждой группой Success
true и часто совпадающий текст дублируется в нескольких группах или пуст. Таким образом, эта структура данных (ниже) не то, что я ожидаю:
Using TRegEx
Regex: (?'word'\b[a-zA-Z]{3,}\b)|(?'id'\b\d{1,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
. Какой бы класс я ни использовал, последний индекс группы (меньше 1 для TRegEx) - это группа захвата, которую я хочу.
По пути мне напомнили, что массивы Pascal часто основаны на 1, а не на 0.
Using TPerlRegEx
Regex: (?'word'\b[a-zA-Z]{3,}\b)|(?'id'\b\d{1,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 0 at 1, value ''
length 4 at 28, value 'B706'