【问题标题】:Is it possible to shortcut a regular expression into matching?是否可以将正则表达式简化为匹配?
【发布时间】:2009-08-03 19:42:48
【问题描述】:

我有一些针对非常长的字符串运行的正则表达式。但是,字符串中唯一与 RE 有关的部分位于开头附近。大多数 RE 类似于:

\\s+?(\\w+?).*

REs 在开头附近捕获了几个组,而不关心字符串的其余部分是什么。出于性能原因,有没有办法让 RE 引擎避免查看终止 .* 消耗的所有字符?

注意:带有 RE 的应用程序是使用 java.regex 类编写的。

编辑:例如,我有以下 RE:

.*?id="number"[^>]*?>([^<]+?).*

这是针对存储为 StringBuilders 的大型 HTML 文件运行的。带有id="number" 的标签始终位于 HTML 文件的开头附近。

【问题讨论】:

  • 请发布您尝试匹配的一些字符串的示例。
  • 我们可以看到您使用的实际代码吗?

标签: java regex performance optimization


【解决方案1】:

使用 java.util.regex 类时,有多种方法可以匹配给定的字符串。 Matcher.matches 总是匹配 整个 输入字符串。 Matcher.find 在输入字符串中查找与您的正则表达式某处 匹配的内容。最后,Matcher.lookingAt 将您的正则表达式与输入字符串的开头相匹配。

如果您使用Matcher.matches,您可能需要末尾的.* 来匹配整个字符串。但是,您最好改用其他方法之一,这样您就可以不用.*。听起来Matcher.lookingAt 可能适合您的目的。

【讨论】:

  • 当我想要Matcher.find 的行为时,我正在使用Matcher.matches,谢谢!
【解决方案2】:

为什么不直接取出。*,你不需要它。

^\\s+?(\\w+?)

【讨论】:

  • 这对我不起作用。删除导致匹配失败的.*
  • 如果 \\s+?(\\w+?).* 整个正则表达式?或者它是更大表达的一部分?
  • Ben,你想匹配什么,以及在什么输入上导致 .* 失败,使用什么特定的 java 代码?
  • 我想既然你选择了 greg 的答案,你正在使用匹配项,这就解释了为什么 michael 的答案对你不起作用。
  • 是的,一旦我切换到find(),就可以删除.*
【解决方案3】:
.*?id="number"[^>]*?>([^<]+?).*

这真的是您使用的正则表达式吗?我问的原因是因为([^&lt;]+?) 总是会匹配一个字符,就好像你写了([^&lt;]) 一样。 + 量词必须至少匹配一次,但由于它不情愿,它会立即移交给下一部分 - .* - 这总是成功的。删除.* 并切换到find()lookingAt() 也不会改变这种行为(尽管获得相同结果可能会更快一些)。如果你想匹配所有文本直到下一个尖括号,你应该去掉问号:([^&lt;]+)

[^&gt;]*?&gt; 也没有多大意义。在匹配括号之前,您必须消耗尽可能多的非括号,那么使该量词不情愿的意义何在?事实上,让它变得贪婪也没有意义。如果[^&gt;]* 尽可能匹配并且下一个字符不是'>',那么你知道回溯不会有任何好处。如果您的正则表达式支持它们,您不妨使用所有格量词 - [^&gt;]*+&gt; - 或原子组 - (?&gt;[^&gt;]*+)&gt;

第一个量化部分 - .*? - 是唯一正确使用的部分(如果不是最佳使用)。将其放在正则表达式的开头模拟find() 在您使用lookingAt() 或(末尾带有.*matches() 时的行为。但是,正如您所发现的,将其关闭并使用 find() 会更有效。

不情愿的量词非常方便,但最近似乎它们曝光过度了。随着频率的增加,我看到人们给出“使用不情愿的量词”的建议,没有任何解释或限定——这只是另一个灵丹妙药。我相信这个问题中的正则表达式就是结果。三个不情愿的量词,一个应该是贪婪的,一个应该是所有格的,另一个根本不应该存在。

编辑:这里有一个例子来说明我在说什么,并解决斯蒂芬 C 的评论。给定这个字符串:

<div id="number" class="whatever">abc123</div>

...正则表达式的动态部分匹配如下:

.*?         => '<div '

[^>]*?      => ' class="whatever"'

([^<]+?)    => 'a'

.*          => 'bc123</div>'

将所有不情愿的量词更改为贪婪不会改变整体匹配(整个字符串),也不会改变前两个动态部分匹配的内容。但最后两个被重新分配:

([^<]+)     => 'abc123'

.*          => '</div>'

查看原始的正则表达式,我认为这一定是想要的结果;如果不捕获整个内容'abc123',为什么还要在捕获组中使用如此复杂的子表达式?这就是让我相信不情愿的量词被盲目使用的原因。

另一件事:回顾线程,我看到 OP 实际上并没有他在切换到 find() 时从正则表达式的前面删除了 .*?方法。 @Ben,如果您还没有这样做,那么您应该这样做;现在只是放慢速度。这会让你得到这个正则表达式:

id="number"[^>]*+>([^<]+)

我也不希望任何人认为我在质疑公认的答案。我只是对过度使用/不当使用不情愿的量词感到不满。

【讨论】:

  • 使用贪婪与不情愿的量词只会影响尝试替代方案的顺序。如果模式不匹配,正则表达式引擎必须尝试所有替代方案。
  • @Stephen:是的,但它可以改变正则表达式的哪些 parts 匹配输入的哪些部分。我在答案中添加了一个示例;评论太复杂了(即使有所有这些不错的新格式选项)。
  • relucant 修饰符还强制正则表达式引擎为前进的每一步查询下一个可能的匹配项,可能会导致大量不需要的分支和回溯,如本例所示。
【解决方案4】:

如果您正在处理 HTML,除非您对数据文件有 100% 的控制权,否则正则表达式不是进行分析的正确工具。它最终会破裂。

在我看来,您需要具有 id="number" 并且显然更多的标签的内容。存在允许对 HTML 输入进行 XSLT 转换的宽松解析器,这可能正是您所需要的。有兴趣的我去看看。

【讨论】:

  • 不幸的是,除非您可以控制您的数据文件,否则没有合适的工具来解析 HTML。世界上到处都是破坏各种规范的 HTML 文件,并且会绊倒任何常规的 Java HTML 解析器。例如,如果 HTML 不能作为(格式正确的)XML 读取,则 XSLT 转换将失败。
  • 使用正则表达式是我们做出的设计决定。正则表达式使我们在查找数据的方式上有些模糊。而特定于 HTML 的解决方案通常期望标记具有具有精确 ID 和已知元素层次结构的特定布局。
  • @Stephen C,当我说“存在允许带有 HTML 输入的 XSLT 的宽松解析器”时,我的意思是实际上存在构建 DOM 树或 SAX 流的 HTML 解析器。我记得曾经读过 Swing 中的 HTML 解析器可以调整来执行此操作。如果我指的是格式良好的 XHTML,我会这么说 :-)
  • @Ben S,XPath 表达式允许您轻松选择节点的文本,该节点的属性名为“id”,值为“number”。
  • 这样的Java答案。他关心的是需要解析整个字符串的正则表达式。您将整个字符串解析为 DOM 的答案,以便您可以进行搜索。我什至不想从 SAX 替代方案开始。不要误会我的意思,我完全同意你的观点,但它并不适用于全球。如果您要分解整个 HTML 或 XML 文档,正则表达式不是这样做的方法。只需对他正在做的事情进行简短的 sn-p,正则表达式就是这项工作的正确答案。它将更快、更可靠地从文档中提取一条信息。
【解决方案5】:

有一个很棒的库可以处理 HTML 文件——包括格式错误的真实世界文件:BeautifulSoup http://www.crummy.com/software/BeautifulSoup/

用这个库很容易找到你的 id= 标签

【讨论】:

    【解决方案6】:

    在这种特殊情况下,简单的答案是使用“查找”而不是“匹配”。但是,如果这对您不起作用,Java Pattern 类支持带有所谓的所有格量词的正则表达式,可用于防止回溯。

    占有量词是贪婪和不情愿量词的第三种选择。 Java 中的语法是 'X?+' 或 'X*+' 或 'X++'。所有格量词匹配尽可能多的字符(如贪婪量词),但如果模式的其余部分不匹配,所有格量词就会失败而不是退出。 (有点像 Prolog 中的“剪辑”。)

    但请注意,使用所有格量词而不是贪婪或不情愿的量词会改变模式的含义。

    this page有关于所有格量词的教程信息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-02
      • 2010-10-24
      • 2018-04-28
      • 2019-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多