我有一个应用程序,用户可以在许多地方指定正则表达式.这些在运行应用程序时用于检查文本(例如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匹配是非常困难的,因此通常的方法是使用两个正则表达式:一个用于指定必须匹配的模式,另一个用于指定不能匹配的模式.如果第一个匹配而第二个匹配则文本匹配.在我的应用程序中,添加在用户现在可以提供的每个地方都有第二个正则表达式的能力将是很多工作,因此我想扩展正则表达式语法,并说出' 并且不包含 模式 ' .
您不需要引入新符号.已经支持大多数正则表达式引擎所需的内容.这只是学习和应用它的问题.
您对性能有顾虑,但是您是否对其进行了测试?您是否测量并证明了这些性能问题?它可能会很好.
在许多不同的场景中,正则表达式适用于许多人.它也可能符合您的要求.
此外,您可以简化在另一个SO问题上找到的复杂正则表达式.负面和正面的前瞻和外观都有简单的表达方式.
?!
?
?=
?<=
一些例子
假设示例文本是 Albatross
鉴于以下正则表达式,您将看到以下结果:
tr
- 匹配
td
- 匹配
^td
- 没有比赛
^tr
- 没有比赛
^
- 匹配 - 匹配
^
- 没有比赛.* ^
^
- 没有比赛) - 匹配
(?)^
^
^
^(?!.*Albatross.*)
- 没有比赛
说明
前两个匹配,因为正则表达式可以应用于样本(或测试)字符串中的任何位置.后两个不匹配,因为^表示"从头开始",测试字符串不以td或tr开头 - 它以左尖括号开头.
第五个示例匹配,因为测试字符串以 使用时,第8个正则表达式在正则表达式的末尾应用正向lookbehind断言 第九个例子展示了如何使用插入负向lookbehind断言 第十个示例使用另一个负面的lookbehind断言.基本上它表示"允许正则表达式匹配,如果此时光标后面的正确,与parens中的内容不匹配,在这种情况下
匹配并包括字符串的结尾.检查字符串末尾的"Albatross"会产生负匹配,因为测试字符串结束.第六个没有,因为它希望样本字符串开头
匹配,包括结束的字符串.由于图案,紧跟在后面的一个闭合角括号 tr
,但在实际的测试字符串中,开口tr
包含valign
属性,所以后面tr
是一个空格.第7个正则表达式显示了如何使用通配符来允许空间和属性.
?<
.它说,只有当测试字符串中的光标前面的内容与匹配中的内容匹配时才匹配整个正则表达式?<
.在这种情况下,接下来是tr>
.在评估``^.*, the cursor in the test string is positioned at the end of the test string. Therefore, the
tr>`之后,匹配测试字符串的末尾,其结果为TRUE.因此,正向后观评估为真,因此整体正则表达式匹配.?.基本上它说"允许正则表达式匹配,如果此时光标后面的正确
?,与parens 中的后续内容不匹配,在这种情况下是
tr>
.在断言前的正则表达式^
tr>
不匹配字符串的结尾.但是,这是一种消极的断言,因此它的计算结果为FALSE,这意味着第九示例不匹配.
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"
AND "lion"
AND 的字符串"tiger"
,但不包含"dog"
OR "wolf"
OR "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()方法来逃避它可以包含任何元字符(即(
,)
,|
等等).此外,为了便于阅读,上述正则表达式以自由间隔模式编写.您的生产正则表达式应该不使用此模式下,用户子之内,否则将空白被忽略.
\b
如果子串只包含真实的单词,您可能希望在每个断言中的每个"单词"之前和之后添加单词边界.
另请注意,使用以下替代语法可以使负面断言更有效:
(?!.*?(?:dog|wolf|hyena))