【问题标题】:Regex is behaving lazy, should be greedy正则表达式表现得很懒惰,应该是贪婪的
【发布时间】:2010-03-07 02:23:41
【问题描述】:

我认为默认情况下我的 Regex 会表现出我想要的贪婪行为,但它不在以下代码中:

 Regex keywords = new Regex(@"in|int|into|internal|interface");
 var targets = keywords.ToString().Split('|');
 foreach (string t in targets)
    {
    Match match = keywords.Match(t);
    Console.WriteLine("Matched {0,-9} with {1}", t, match.Value);
    }

输出:

Matched in        with in
Matched int       with in
Matched into      with in
Matched internal  with in
Matched interface with in

现在我意识到,如果我只是按长度降序对关键字进行排序,我可以让它在这个小例子中工作,但是

  • 我想了解为什么会这样 没有按预期工作,并且
  • 我正在做的实际项目 在正则表达式中有更多的词和 把它们留在里面很重要 按字母顺序。

所以我的问题是:为什么这是懒惰的,我该如何解决?

【问题讨论】:

  • 我不确定您的实际用法是否更复杂,但如果上面的示例实际上是您正在做的事情,我认为循环遍历查找的单词列表会好一千倍与 IndexOf 方法匹配。如果正则表达式只是交替包含一堆单词,性能可能会很糟糕。
  • @Josh - 不,示例已简化。实际的应用程序正在读取语言文件以生成词法分析器和语法分析器。我的正则表达式有点生疏;我的问题现在看起来很明显!
  • @Josh:正则表达式引擎可以针对这种情况进行很多优化,包括在未能匹配公共前缀后丢弃许多检查。例如,如果第一个字符不是“i”,则不会检查以“i”开头的任何分支。不确定 .NET 引擎是否会这样做,但如果没有,我会感到惊讶。
  • @Max,它确实构建了状态转换以加快匹配速度。如果 .Net 与其他长期建立和完善的正则表达式引擎相比,这是我所收集到的争论的问题。但它确实比 IndexOf 表现更好。 (我已经在工作中循环运行,以证明为什么同事应该使用正则表达式而不是 IndexOf... 根据匹配的内容,您可以获得数量级的速度提升。)

标签: regex greedy non-greedy regex-greedy alternation


【解决方案1】:

惰性和贪婪仅适用于量词(?*+{min,max})。交替总是按顺序匹配并尝试第一个可能的匹配。

【讨论】:

  • 除了重新订购没有其他选择了吗?嗯……我想我可以即时重新排序,这样我就可以按字母顺序保留定义……
  • @Stomp:是的,可以做到。在程序中保持列表按字母顺序排列,在实际应用之前,您可以按长度对其进行排序。
【解决方案2】:

您似乎是在尝试断句。为此,您需要整个表达式正确,而您当前的表达式不是。试试这个吧..

new Regex(@"\b(in|int|into|internal|interface)\b");

“\b”表示匹配单词边界,并且是零宽度匹配。这是依赖于语言环境的行为,但通常这意味着空格和标点符号。作为一个零宽度匹配,它不会包含导致正则表达式引擎检测到单词边界的字符。

【讨论】:

  • 添加\b 将引发所需的行为,但您误解了它的工作原理。 \b 是一个零宽度断言,如 ^$ 和环视;它不是匹配一个字符,而是匹配一个字符之前或之后的假想间隙。如果第一个或最后一个字符(分别)是单词字符,则字符串的开头或结尾自动成为单词边界,因此您的第二个正则表达式只是第一个正则表达式的更详细版本。
  • @Alan,我尝试执行代码,显然你是对的。我需要仔细检查工作中的代码,看看我们在那里做了什么……也许我们使用的是\W而不是\b。我知道我们在类似的情况下得到了某种“非单词”字符,我知道我们设置了一些时髦的中午捕捉组。至于它是区域敏感的,这将是这种情况,因为单词边界将根据标点符号的作用而不同。
  • @Alan,我修改了答案以反映您的反馈。
【解决方案3】:

根据RegularExpressions.info,正则表达式为eager。因此,当它通过您的piped expression 时,它会在第一个稳定匹配时停止。

我的建议是将所有关键字存储在一个数组或列表中,然后在需要时生成已排序的管道表达式。只要您的关键字列表不变,您只需执行一次。只需将生成的表达式存储在某种单例中并在正则表达式执行时返回。

【讨论】:

  • @Jeras - 感谢您的链接!我在 MSDN 上搜索,一定错过了它急切地寻找第一个匹配项。
猜你喜欢
  • 1970-01-01
  • 2012-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-19
相关资源
最近更新 更多