将正则表达式语法扩展为“不包含文本XYZ”

我有一个应用程序,用户可以在许多地方指定正则表达式。 这些在运行应用程序时用于检查文本(例如URL和HTML)是否与正则表达式匹配。 用户通常希望能够说出文本与ABC匹配的位置并且与XYZ不匹配 。 为了方便他们这样做,我想在我的应用程序中扩展正则表达式语法,并说出’ 并且不包含模式 ‘。 有什么建议可以做到这一点吗?

我的应用程序是用C#.NET 3.5编写的。

我的计划(在我得到这个问题的真棒答案之前……)

目前我正在考虑使用¬字符:在¬字符是正常正则表达式之前的任何内容,¬字符之后的任何内容都是在要测试的文本中无法匹配的正则表达式。

所以我可能会使用像这样(人为的)示例的一些正则表达式:

on (this|that|these) day(s)?¬(every|all) day(s) ? 

例如, 在这一天,男人说…… “但是在这一天和之后的每一天都不会匹配…… ”。

在我处理正则表达式的代码中,我将简单地拆分正则表达式的两个部分并分别处理它们,例如:

  public bool IsMatchExtended(string textToTest, string extendedRegex) { int notPosition = extendedRegex.IndexOf('¬'); // Just a normal regex: if (notPosition==-1) return Regex.IsMatch(textToTest, extendedRegex); // Use a positive (normal) regex and a negative one string positiveRegex = extendedRegex.Substring(0, notPosition); string negativeRegex = extendedRegex.Substring(notPosition + 1, extendedRegex.Length - notPosition - 1); return Regex.IsMatch(textToTest, positiveRegex) && !Regex.IsMatch(textToTest, negativeRegex); } 

有关更好地实施此类扩展的任何建议吗? 我需要稍微聪明一点,在¬字符上拆分字符串以允许它被转义,所以不要只使用上面简单的Substring()拆分。 还有什么需要考虑的吗?

替代计划

在写这个问题时,我也遇到了这个答案 ,建议使用这样的东西:

 ^(?=(?:(?!negative pattern).)*$).*?positive pattern 

因此,当他们想要不匹配某些文本时,我可以建议人们使用类似的模式,而不是我原来的计划。

这会与我原来的计划相同吗? 我认为这是一种相当昂贵的方法,因为我有时会解析大型html文档这可能是一个问题,而我认为我的原始计划会更高效。 任何想法(除了明显的:’尝试两个并测量它们!’)?

可能与性能相关:有时会有几个“单词”或更复杂的正则表达式不能在文本中,如上面的例子中的(every | all),但有一些更多变化。

为什么!?

我知道我原来的方法看起来很奇怪,例如为什么不只是有两个正则表达式?? 但是在我的特定应用程序中,管理员提供了正则表达式,并且很难让它们能够在当前提供它们的地方提供两个正则表达式。 在这种情况下,使用NOT的语法要容易得多 – 只要相信我就这一点。

我有一个应用程序,允许管理员在各种配置点定义正则表达式。 正则表达式仅用于检查文本或URL是否与特定模式匹配; 不进行替换,也不使用捕获组。 但是,他们通常希望指定一种模式,说明“ABC不在文本中”。 在正则表达式中进行NOT匹配是非常困难的,因此通常的方法是使用两个正则表达式:一个用于指定必须匹配的模式,另一个用于指定必须不匹配的模式。 如果第一个匹配而第二个匹配则文本匹配。 在我的应用程序中,添加在用户现在可以提供的每个地方都有第二个正则表达式的能力将是很多工作,因此我想扩展正则表达式语法,并说出’ 并且不包含模式 ‘ 。

您不需要引入新符号。 已经支持大多数正则表达式引擎所需的内容。 这只是学习和应用它的问题。

您对性能有顾虑,但是您是否对其进行了测试? 您是否测量并certificate了这些性能问题? 它可能会很好。

在许多不同的场景中,正则表达式适用于许多人。 它也可能符合您的要求。

此外,您可以简化在另一个SO问题上找到的复杂正则表达式。 负面和正面的前瞻和外观都有简单的表达方式。
?! ? ?= ?<=


一些例子

假设示例文本是

Albatross

鉴于以下正则表达式,您将看到以下结果:

  1. tr - 匹配
  2. td - 匹配
  3. ^td - 不匹配
  4. ^tr - 不匹配
  5. ^ - 匹配
  6. ^
    .*

    - 不匹配

  7. ^.* - 匹配
  8. ^.*(?
    )

    - 匹配

  9. ^.*(?) - 不匹配
  10. ^.*(? - 匹配
  11. ^.*(? - 不匹配
  12. ^(?!.*Albatross.*).* - 不匹配

说明

前两个匹配,因为正则表达式可以应用于样本(或测试)字符串中的任何位置。 后两个不匹配,因为^表示“从头开始”,测试字符串不以td或tr开头 - 它以左尖括号开头。

第五个示例匹配,因为测试字符串以开头。 第六个没有,因为它希望样本字符串以

开头,并且紧跟在tr后面有一个结束角括号,但在实际的测试字符串中,开头tr包含valign属性,所以后面的tr是一个空格。 第7个正则表达式显示了如何使用通配符来允许空间和属性。

第8个正则表达式在正则表达式的末尾应用正向lookbehind断言,使用?< 。 它说,只有当测试字符串中光标紧跟在光标之前,才匹配整个正则表达式,匹配在parens中的内容,跟随?< 。 在这种情况下,接下来是tr> 。 在评估``^。*之后, the cursor in the test string is positioned at the end of the test string. Therefore, the , the cursor in the test string is positioned at the end of the test string. Therefore, the tr>`与测试字符串的末尾匹配,后者的计算结果为TRUE。 因此,正面后观评估为真,因此整体正则表达式匹配。

第九个例子展示了如何使用?插入否定的lookbehind断言 。 基本上它说“允许正则表达式匹配,如果此时光标后面的内容与下面的内容不匹配?在parens中,在这种情况下是tr> 。在断言之前的正则表达式位, ^.*匹配并包括字符串的结尾。因为模式tr>匹配字符串的结尾。但这是一个否定断言,因此它的计算结果为FALSE,这意味着第9个示例不匹配。

第十个示例使用另一个负面的lookbehind断言。 基本上它说“允许正则表达式匹配,如果此时光标后面的内容与parens中的内容不匹配,在本例中为Albatross 。断言之前的正则表达式位, ^.*匹配并包括字符串的结尾。在字符串末尾检查“Albatross”会产生一个负匹配,因为测试字符串以结尾。因为负面lookbehind的parens中的模式确实不匹配,这意味着负面的lookbehind评估为TRUE,这意味着第10个示例是匹配。

第11个例子将负面观察延伸到包括通配符; 在英语中,负面后看的结果是“只有在前面的字符串不包含单词Albatross时才匹配”。 在这种情况下,测试字符串DOES包括单词,负的lookbehind评估为FALSE,并且第11个正则表达式不匹配。

第12个示例使用负前瞻断言。 与lookbehinds一样,前瞻是零宽度 - 它们不会在测试字符串中移动光标以进行字符串匹配。 在这种情况下的前瞻,立即拒绝字符串,因为.*Albatross.*匹配; 因为它是一个负前瞻,它的计算结果为FALSE,这意味着整个正则表达式无法匹配,这意味着对测试字符串的正则表达式的评估在那里停止。

示例12始终计算与示例11相同的布尔值,但它在运行时的行为不同。 在前12中,首先在停止时立即执行否定检查。 在ex 11中,应用完整的正则表达式,并在检查lookbehind断言之前求值为TRUE。 因此,您可以看到在比较前瞻和后瞻时可能存在性能差异。 哪一个适合您取决于您​​所匹配的内容,以及“肯定匹配”模式和“否定匹配”模式的相对复杂性。

有关这方面的更多信息,请阅读http://www.regular-expressions.info/

或者获取正则表达式评估工具并尝试一些测试。

喜欢这个工具:
在此处输入图像描述

源和二进制

您可以使用单个正则表达式轻松完成目标。 这是一个演示了一种方法的例子。 此正则表达式匹配包含"cat""lion""tiger"的字符串,但不包含"dog""wolf""hyena"

 if (Regex.IsMatch(text, @" # Match string containing all of one set of words but none of another. ^ # anchor to start of string. # Positive look ahead assertions for required substrings. (?=.*? cat ) # Assert string has: 'cat'. (?=.*? lion ) # Assert string has: 'lion'. (?=.*? tiger ) # Assert string has: 'tiger'. # Negative look ahead assertions for not-allowed substrings. (?!.*? dog ) # Assert string does not have: 'dog'. (?!.*? wolf ) # Assert string does not have: 'wolf'. (?!.*? hyena ) # Assert string does not have: 'hyena'. ", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace)) { // Successful match } else { // Match attempt failed } 

你可以看到所需的模式。 在组装正则表达式时,请确保通过Regex.escape()方法运行每个用户提供的子字符串,以转义它可能包含的任何元字符(即()| etc)。 此外,为了便于阅读,上述正则表达式以自由间隔模式编写。 您的生产正则表达式不应使用此模式,否则将忽略用户子字符串中的空格。

如果子字符串仅包含真实单词,则可能需要在每个断言中的每个“单词”之前和之后添加\b字边界。

另请注意,使用以下替代语法可以使负面断言更有效:

(?!.*?(?:dog|wolf|hyena))