如何处理可能为空的TR​​egEx命名捕获组?

时间:2015-12-22 21:58:34

标签: delphi delphi-10-seattle

我有一个带有命名捕获组的正则表达式,其中最后一个组是可选的。我无法弄清楚如何迭代这些组并在空的时候正确处理可选组;我收到 EListOutOfBounds 例外。

正则表达式正在解析由我们通过电子邮件接收的外部系统生成的文件,该文件包含有关已向供应商发出的检查的信息。该文件是以管道分隔的;样本在下面的代码中。

program Project1;

{$APPTYPE CONSOLE}

uses
  System.SysUtils, System.RegularExpressions, System.RegularExpressionsCore;
{
  File format (pipe-delimited): 
   Check #|Batch|CheckDate|System|Vendor#|VendorName|CheckAmount|Cancelled (if voided - optional)
}
const 
  CheckFile = '201|3001|12/01/2015|1|001|JOHN SMITH|123.45|'#13 +
              '202|3001|12/01/2015|1|002|FRED JONES|234.56|'#13 +
              '103|2099|11/15/2015|2|001|JOHN SMITH|97.95|C'#13 ;

var
  RegEx: TRegEx;
  MatchResult: TMatch;
begin
  try
    RegEx := TRegEx.Create(
      '^(?<Check>\d+)\|'#10 +
      '  (?<Batch>\d{3,4})\|'#10 +
      '  (?<ChkDate>\d{2}\/\d{2}\/\d{4})\|'#10 +
      '  (?<System>[1-3])\|'#10 +
      '  (?<PayID>[0-9X]+)\|'#10 +
      '  (?<Payee>[^|]+)\|'#10 +
      '  (?<Amount>\d+\.\d+)\|'#10 +
      '(?<Cancelled>C)?$',
      [roIgnorePatternSpace, roMultiLine]);
    MatchResult := RegEx.Match(CheckFile);
    while MatchResult.Success do
    begin
      WriteLn('Check: ', MatchResult.Groups['Check'].Value);
      WriteLn('Dated: ', MatchResult.Groups['ChkDate'].Value);
      WriteLn('Amount: ', MatchResult.Groups['Amount'].Value);
      WriteLn('Payee: ', MatchResult.Groups['Payee'].Value);
      // Problem is here, where Cancelled is optional and doesn't 
      // exist (first two lines of sample CheckFile.)
      // Raises ERegularExpressionError 
      // with message 'Index out of bounds (8)' exception.
      WriteLn('Cancelled: ', MatchResult.Groups['Cancelled'].Value);
      WriteLn('');
      MatchResult := MatchResult.NextMatch;
    end;
    ReadLn;
  except
    // Regular expression syntax error.
    on E: ERegularExpressionError do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

我已尝试检查MatchResult.Groups['Cancelled'].Index是否小于MatchResult.Groups.Count,尝试检查MatchResult.Groups['Cancelled'].Length > 0,然后检查MatchResult.Groups['Cancelled'].Value <> ''是否成功。

如果该组没有匹配,我如何正确处理可选捕获组已取消

2 个答案:

答案 0 :(得分:6)

如果结果中不存在请求的命名组,则会引发$(window).on('resize',function(){ if($(this).width() > 680){ //add remove css styles } }); 异常。这是设计的(尽管异常消息的措辞具有误导性)。如果您在ERegularExpressionError阻止后移动ReadLn(),则会在进程退出之前在控制台窗口中看到异常消息。当引发异常时,您的代码不会等待用户输入。

由于您的其他组不是可选的,因此您可以简单地测试try/except是否足够大以容纳MatchResult.Groups.Count组(测试的字符串位于索引0处的组中,因此它是包含在Cancelled)中:

Count

或者:

if MatchResult.Groups.Count > 8 then
  WriteLn('Cancelled: ', Write(MatchResult.Groups['Cancelled'].Value)
else
  WriteLn('Cancelled: ');
顺便说一下,你的循环也没有调用Write('Cancelled: '); if MatchResult.Groups.Count > 8 then Write(MatchResult.Groups['Cancelled'].Value); WriteLn(''); ,所以你的代码陷入无限循环。

NextMatch()

答案 1 :(得分:6)

您还可以避免使用可选组并使取消组成为强制性组合,包括C或任何内容。只需将正则表达式的最后一行更改为

即可
'(?<Cancelled>C|)$'

对于您的测试应用程序,这不会改变输出。如果您需要进一步使用取消,您只需检查它是否包含C或空字符串。

if MatchResult.Groups['Cancelled'].Value = 'C' then
  DoSomething;