学術的な理由から、正規表現のソリューションも紹介したいと思います。おそらく、これを解決できる唯一の正規表現エンジンを使用しているからです。
.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)
、最後のキャプチャが上書きされるのではなく、スタックにプッシュされます。を使用して、そのスタックから 1 つのキャプチャをポップできます(?<-Depth>)
。スタックが空の場合、これは失敗します (現在の位置で一致しないものと同様)。そして、スタックが空かどうかを で確認できます(?(Depth)patternIfNotEmpty|patternIfEmpty)
。
それに加えて、.NET には、可変長の後読みをサポートする唯一の正規表現エンジンがあります。これら 2 つの機能を一緒に使用できれば、目的の文字列の 1 つの左側を見て~(
、現在の入れ子構造の外にどこかに存在するかどうかを確認できます。
しかし、ここに問題があります (上記のリンクを参照)。後読みは .NET では右から左に実行されます。つまり、閉じ括弧をプッシュし、開き括弧に遭遇するとポップする必要があります。逆ではありません。
ここでは、その残忍な正規表現について説明します (.NET のように、後読みを下から上に読むと理解しやすくなります)。
(?<= # lookbehind
~ # if there is a literal ~ to the left of our string, we're good
| # 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
| # OR
(?<-Depth>)? # pop the top of stack IF possible. the last ? is necessary for
# like "human" where we start with a ( before there was a )
# which could be popped.
[(] # match a literal (
| # 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)
# match one of the words in the check list