【问题标题】:Regex speed in JavaJava中的正则表达式速度
【发布时间】:2011-05-18 06:53:30
【问题描述】:

大量字符串的一些示例挂钟时间:

.split("[^a-zA-Z]"); // .44 seconds
.split("[^a-zA-Z]+"); // .47 seconds
.split("\\b+"); // 2 seconds

对急剧增加的任何解释?我可以将处理器中的 [^a-zA-Z] 模式想象为一组四个比较操作,其中所有四个比较操作都只有在真实情况下才会发生。 \b 呢?有人对此有什么要权衡的吗?

【问题讨论】:

  • 所有这些时间似乎都大了 3 个数量级。你的输入数据是什么样的?
  • 它看起来像几百万行拆分。这是正确的数量级。

标签: java regex optimization


【解决方案1】:

首先,拆分一个或多个零宽度断言是没有意义的! Java 的正则表达式在合理优化方面不是很聪明——而且我是慈善的。

其次,不要在 Java 中使用 \b:它会搞砸并且与 \w 不同步。

有关此内容的更完整说明,尤其是如何使其与 Unicode 一起使用,请参阅this answer

【讨论】:

  • 太空学员时刻;忘了 \b 是零宽度。
【解决方案2】:

\b 是一个与[^A-Za-z] 根本不同的零宽度断言。因为\b 是作为 if/then 实现的(请参阅下面的 tchrist 的评论),所以检查每个字符串中的每个字母可能会做更多的工作。此外,加号会导致回溯,这会成倍增加成本。

此外,当您在单词边界上进行拆分时,与仅在 [^a-zA-Z]+ 上拆分相比,您将匹配更多的位置。这将导致分配更多的字符串,这也将花费更多的时间。 要看到这一点,试试这个程序:

import java.lang.String;

class RegexDemo {
    private static void testSplit(String msg, String re) {
        String[] pieces = "the quick brown fox".split(re);
        System.out.println(msg);
        for (String s : pieces) {
            System.out.println(s);
        }
        System.out.println("----");
    }

    public static void main(String args[]) {
        testSplit("boundary:", "\\b+");
        testSplit("not alpha:", "[^A-Za-z]+");
    }
}

可能不相关,当您使用 String.split() 时,必须为每次使用编译正则表达式。 如果您将正则表达式预编译为模式,例如,

Pattern boundary = Pattern.compile("\\b+");

然后使用boundary.split(testString) 进行拆分,您将节省为每个测试字符串编译正则表达式的成本。因此,可以想象,“\b+”的编译比其他模式的编译要慢,您可以在这里使用预编译的习语进行测试,尽管这在我看来不太可能作为解释。

有关正则表达式性能的更多信息,请阅读 Russ Cox http://swtch.com/~rsc/regexp/ 的这些文章并查看 http://www.regular-expressions.info/

【讨论】:

  • 我不知道 Java 的正则表达式是否支持 Unicode(如果不是,我会感到惊讶),那么 \b更复杂检查不仅仅是[a-zA-Z] 甚至[a-zA-Z0-9_]...
  • 这是\b 永远不会 表示[^a-zA-Z0-9_]。一方面,它有宽度,而\b 没有。 \b 的真正含义是 (?:(?<=\w)(?!\w)|(?<!\w)(?=\w))。当然,这假设你有一个理智的\w 定义。 Java没有,所以你必须使用我的重写函数或` (?:(?
  • @Dean:是的,\b 可以识别 Unicode(以它自己的、有趣的小方式),而且它更复杂的测试。而且它必须应用于每个位置,因为通过或失败,它从不消耗任何字符。
  • split 只编译一次模式:public String[] split(String regex, int limit) { return Pattern.compile(regex).split(this, limit); } 来自j2se/src/share/altclasses/java/lang/String.java
  • @Platinum:请记住有两种拆分方法:Pattern#splitString#split。重复调用Pattern#split 不会导致重新编译模式。反复调用String#split 会导致重新编译模式。
猜你喜欢
  • 2018-04-02
  • 1970-01-01
  • 2021-12-18
  • 1970-01-01
  • 1970-01-01
  • 2015-12-29
  • 2015-04-27
  • 1970-01-01
  • 2016-09-28
相关资源
最近更新 更多