为什么这个正则表达式匹配?

时间:2018-03-15 19:19:07

标签: c# regex

下午好!我尝试编写的模式仅在字符串包含给定格式的日期或两个匹配短语中的任何一个时才匹配:

(?=(?<p0>(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[ ](\d+)(,)[ ](\d+)[ ](\d+):(\d+):(\d+) (PM|AM)))?(?=(?<p1>MATCHINGPHRASE2))?(?=(?<p2>MATCHINGPHRASE3))?

我有一个方法调用使用此模式的正则表达式:

internal bool IsSubjectRecognized(string subject)
    {
        var match = _regEx.Match(subject);
        if (match.Success)
        {
            return true;
        }
        return false;
    }

最后,我有一个单元测试,以确保上面的方法将为不包含日期的字符串返回false,或者匹配短语看起来像这样:

public void IsSubjectRecognizedRejectsReggoStrings()
    {
        var subject = "Watch out for the medalions. My diamonds are wreckless.";
        var rules = new MatchingRules();
        Assert.IsFalse(rules.IsSubjectRecognized(subject));
    }

为什么这会返回真实?!

1 个答案:

答案 0 :(得分:0)

让我们解开那个正则表达式,好像使用perl的/x修饰符一样(我已将空格转换为\x20,因为/x跳过空格和注释):

(?=
  (?<p0>
    (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)
    [\x20]
    (\d+)
    (,)
    [\x20]
    (\d+)
    [\x20]
    (\d+):(\d+):(\d+)\x20(PM|AM)
  )
)?
(?=
  (?<p1>MATCHINGPHRASE2)
)?
(?=
  (?<p2>MATCHINGPHRASE3)
)?

让我们简化一下,看看外层发生了什么:

(?= (?<p0>DATEREGEX) )?
(?= (?<p1>MATCHINGPHRASE2) )?
(?= (?<p2>MATCHINGPHRASE3) )?

这会创建三个可选零宽度前向前瞻,因此它应匹配任何内容。

考虑(使用perl,因为它很容易在命令行上演示):

$ echo foo |perl -ne 'print if /(?=nope)?/'
foo

如果foo(?=nope)?匹配,则应打印。它打印出来,所以有一场比赛。

前向前瞻通常用于向前跳过以确保存在某些内容。我认为你不想在这里做到这一点,但如果没有一些应该和不应该匹配的示例文本,我无法确定。

你可能只想要这样的东西:

(?<p0>
  (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)
  [\x20]
  (\d+)
  (,)
  [\x20]
  (\d+)
  [\x20]
  (\d+):(\d+):(\d+)\x20(PM|AM)
)
(?<p1>MATCHINGPHRASE2)
(?<p2>MATCHINGPHRASE3)

或者,全部再次崩溃:

(?<p0>(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d+)(,) (\d+) (\d+):(\d+):(\d+)\x20(PM|AM))(?<p1>MATCHINGPHRASE2)(?<p2>MATCHINGPHRASE3)

(您也不需要这些字符类; (以及\x20[\x20])与[ ]完全相同,除非出现粘贴问题并且存在某些问题我错过了那个课程。相反,你可能想要\s[[:space:]],这样你就可以覆盖所有空白字符,但我会把它留给你。)

另见: