2

作为个人学习练习,我编写了这个正则表达式来将一元字符串拆分为长度增加 2 次方的部分(另见 ideone.com):

    for (String s :
       new String(new char[500])
       .split("(?=(.*))(?<=(?<=(^.*))\\G.*)(?<=(?=\\2\\2.\\1)^.*)")
    ) {
       System.out.printf("%s ", s.length());
    }
    // prints "1 2 4 8 16 32 64 128 245 "

这结合了环视期间的捕获、嵌套环视、反向引用匹配和无限长的后视(Java 中不正式支持但无论如何都可以使用)。也利用了 2 的幂和的属性以及字符串具有一元字母表的事实。

这个解决方案既不可读,性能也很糟糕。

我的问题是,你将如何“优化”这个正则表达式?

  • 你能让正则表达式更具可读性吗(如果性能更差也没关系)
  • 你能让正则表达式表现得更好吗(如果它不那么可读也没关系)
4

3 回答 3

2

我不是 Java 人,因此我的答案基于 .NET Regex 实现。我用了

"(?<=(^.*)\\G.*)(?<=\\G\\1.)"

基于这样一个事实\sum_{i=0}^{n} 2^n = 2^{n+1} - 1。它基本上是“匹配每个位置,最后一场比赛之后的部分比最后一场比赛之前的部分长一个”。

它的速度大约是原来的两倍(再次在 .NET 上),拆分 10.000 个字符只需不到 2 秒,我认为它更具可读性。嗯...不太可读。=)

干杯! 好问题!=)

编辑:再次查看您的正则表达式,在我看来您使用的是相同的方法,但方式更复杂。我承认在尝试制定自己的解决方案之前我没有尝试阅读您的内容,既因为我喜欢挑战,也因为您的正则表达式非常难以阅读。=) 由于 Java 正则表达式引擎,这些嵌套的环视是否必要?

于 2010-07-07T10:25:18.503 回答
1

我真的不会。我会把整个事情扔掉,然后把它重做为可读的程序代码。

有些事情你真的不应该用正则表达式做。这是其中之一。我完全是为了教育自己,但你真的认为这会在某个时候派上用场吗?

也许你最好学习一些实际可用和可维护的东西:-)

于 2010-07-07T09:31:18.647 回答
0

这些是在 Java 中对我有用的模式。我最终会用完整的解释将所有内容修改为一个全面的答案。这些都是 Java 字符串文字表示。

  • P000:"(?=(.*))(?<=(?<=(^.*))\\G.*)(?<=(?=\\2\\2.\\1)^.*)"
  • P001:"(?=(.*))(?<=(?<=(^.*))\\G.*)(?<=(?=\\2.\\1)\\G.*)"
    • 本质上与 P000 相同,但不是 ,而是^\2\G\2.\1我们将头部砍掉\G\2.\1
    • 2.21 秒内500 ( ideone.com )
  • P002:"(?=(.*))(?<=(?<=(^.*))(?=\\2.\\1)\\G.*)"
    • 比 P000 慢很多但更短
    • 本质上是 P001 的“重构”,现在两个后视都锚定在\G
    • 3.05 秒内400 ( ideone.com )
于 2010-07-07T11:27:25.670 回答