【问题标题】:Regex to match innermost set of parentheses containing a specific character正则表达式匹配包含特定字符的最里面的括号集
【发布时间】:2018-05-16 10:13:11
【问题描述】:

获取包含特定字符的最里面的一组括号的正则表达式是什么? '|'在这种情况下?

一些例子和一个(c#)测试方法:

string[] tests = {
    "x () y", "",
    "x (a) y", "",
    "x (a.b()) y", "",
    "x ((a).b() | (b).c()) y", "(a).b() | (b).c()",
    "x (a|b) y", "a|b",
    "x ((a|b) | c)", "a|b",
    "x (a|b|c) y", "a|b|c",
    "x (a|a.b()|c) y", "a|a.b()|c",
    "x (a.b()|b.c()) y", "a.b()|b.c()",
    "x (a.b()|b.c()|c) y", "a.b()|b.c()|c",
    "x (a|b.c()|c.d()) y", "a|b.c()|c.d()",
    "x (a|(b.c()|d)) y", "b.c()|d",
    "x (a|a.b(a)|c) y", "a|a.b(a)|c"
};

for (int i = 0; i < tests.Length; i+=2)
{
    var match = re.Match(tests[i]);
    var result = match.Groups[1].Value;
    Assert.That(result, Is.EqualTo(tests[i + 1]));
}

【问题讨论】:

标签: c# .net regex


【解决方案1】:

解决所有测试的“非常简单”的正则表达式:

var re = new Regex(@"
(?:\()
(
    (?>
        (?:  
            (?<p>\()  |  
            (?<-p>\))  |  
            [^()|]+  |  
            (?(p)(?!))(?<pipe>\|)  
        )*  
    )    
)
(?:\))
(?(p)(?!))
(?(pipe)|(?!))", RegexOptions.IgnorePatternWhitespace);

string result = match.Groups[1].Value;

注意RegexOptions.IgnorePatternWhitespace 的使用。正则表达式基于balancing groups。基于你不应该尝试使用你不完全理解的正则表达式这一事实,我将省略关于它如何工作的确切解释。我只会说检查 (?(pipe)|(?!)) 检查是否在捕获中至少捕获了一个 |,而 (?(p)(?!)) 表示“(?&lt;p&gt;\() 表达式仍然没有捕获到开括号”。

我对这个正则表达式的看法是,它是在正则表达式中徒劳且危险的练习! (如果不清楚我属于Some people, when confronted with a problem, think "I know, I'll use regular expressions" Now they have two problems. 思想流派)。你不应该使用它。这是不可调试的代码恐怖。

附加内容:此正则表达式大量回溯...添加了 (?&gt; ... ) 以禁用回溯。

回溯的附加测试(第一个带有不平衡括号):

"((((amusemen).emoadj().cap()(, (are we |arent we|I gather)|)?)", "are we |arent we|I gather",
"((amusemen).emoadj().cap()(, (are we |arent we|I gather)|)?)", "are we |arent we|I gather",

【讨论】:

  • 这个正则表达式确实适用于测试输入集。但是,如果使用其他输入(例如,请参阅 this case),它会导致我的系统挂起长达 30 秒。有人可以验证他们得到相同的行为吗?看起来很奇怪
  • @rednoyz 不平衡括号(有 8x ( 和 6x ) )
  • @rednoyz 添加了(&gt; ... ) 以禁用回溯。现在,您提出的“错误”字符串和该字符串的更正版本都是即时的。
【解决方案2】:

以下方法可能过于复杂且有优化空间,但您可以将其作为起点,以防找不到更好的替代方案。我添加了一些基本的括号平衡检查。

该方法通过了您的所有用例,但有几个您没有考虑过的有趣的用例;这种方法解决它们如下:

  • a|b(括号外的字符):被忽略,所以返回一个空字符串
  • (a|b) c|d:返回a|b(同上)
  • (a|b) (c|d)(多于一组匹配):返回c|d(找到的最后一个)
string FindInnermostSet(string source, char toSearch = '|') { var 候选人OpeningParenthesisPosition = -1; var CandidateClosingParenthesisPosition = -1; var CandidateOpeningParenthesNestingLevel = -1; var openingParenthesisPositions = new Stack(); for(int i=0; i= CandidateOpeningParenthesNestingLevel) { 候选人OpeningParenthesNestingLevel = openingParenthesisPositions.Count(); 候选人OpeningParenthesisPosition = openingParenthesisPositions.Peek(); } } 如果(打开括号位置。任何()) throw new Exception("语法错误:缺少')'"); 如果(candidateOpeningParenthesisPosition == -1) 返回 ””; 返回源.子字符串( 候选OpeningParenthesisPosition+1, 候选人ClosingParenthesisPosition-candidateOpeningParenthesisPosition-1); }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-30
    • 2017-10-02
    • 2019-01-05
    • 1970-01-01
    • 1970-01-01
    • 2017-07-02
    相关资源
    最近更新 更多