仅出于学术目的,我也想呈现正则表达式解决方案,主要是因为您可能正在使用唯一能够解决此问题的正则表达式引擎。
在澄清了一些有关.NET独特功能组合的有趣问题之后,以下是可以获得所需结果的代码:
string mainString = @"~(Homo Sapiens means (human being)) or man or ~woman";
List<string> checkList = new List<string> { "homo sapiens", "human", "man", "woman" };
// build subpattern "(?:homo sapiens|human|man|woman)"
string searchAlternation = "(?:" + String.Join("|", checkList.ToArray()) + ")";
MatchCollection matches = Regex.Matches(
mainString,
@"(?<=~|(?(Depth)(?!))~[(](?>[^()]+|(?<-Depth>)?[(]|(?<Depth>[)]))*)"+searchAlternation,
RegexOptions.IgnoreCase
);
现在这是怎么工作的?首先,.NET支持平衡组,允许检测正确嵌套的模式。每次我们捕获一个带有命名捕获组(如
(?<Depth>somepattern)
)的内容时,它不会覆盖上一次的捕获,而是被推送到堆栈中。我们可以使用
(?<-Depth>)
从该堆栈中弹出一个捕获。如果堆栈为空(就像当前位置不匹配的东西一样),这将失败。我们可以使用
(?(Depth)patternIfNotEmpty|patternIfEmpty)
来检查堆栈是否为空。
此外,.NET是唯一支持变长回顾后发机制的正则表达式引擎。如果我们能将这两个特性结合起来,我们就可以查看我们想要的字符串左侧并查看当前嵌套结构之外是否有~(
。
但这里有一个问题(请参见上面的链接)。在.NET中,回顾后发机制是从右到左执行的,这意味着我们需要在遇到左括号时推送右括号,并在遇到右括号时弹出左括号,而不是相反。
因此,这里是一些关于那个致命正则表达式的解释(如果您像.NET一样从下往上阅读回溯部分,则更容易理解):
(?<=
~
| # OR
(?(Depth)(?!)) # if there is something left on the stack, we started outside
# of the parentheses that end end "~("
~[(] # match a literal ~(
(?> # subpattern to analyze parentheses. the > makes the group
# atomic, i.e. suppresses backtracking. Note: we can only do
# this, because the three alternatives are mutually exclusive
[^()]+ # consume any non-parens characters without caring about them
|
(?<-Depth>)?
[(]
| # OR
(?<Depth>[)]) # match a literal ) and push it onto the stack
)* # repeat for as long as possible
) # end of lookbehind
(?:homo sapiens|human|man|woman)
和后面单词之间的空格,并根据空格分割句子,对于以开头的拆分单词,检查列表中的单词是否存在其中。 - user1801163