如何在Java中使用正则表达式查找字符串中最后一组字符?

时间:2019-06-04 09:14:26

标签: java regex regex-greedy

我需要找到字符串中字符集的最后一个索引。考虑字符集为 x,y,z ,字符串为 Vereador Luiz Pauly Home ,那么我需要索引为 18

因此,为了找到索引,我创建了一个带有 DOTALL 标志和贪婪量词作为(?s)的模式。*(x | y | z) 。当模式应用于该字符串(多行)时,我可以从起始组中找到索引。代码:

AND wp_posts.post_type = 'lp_course' AND ((wp_posts.post_status = 'publish'))OR wp_terms.name LIKE '%cantieri%'

如预期的那样,如果匹配,它将正确返回值。

  

但是,如果没有匹配项,那么它花费的时间太长(60万个字符需要17分钟),因为这是贪婪的匹配项。

我尝试了其他量词,但无法获得所需的输出。 那么任何人都可以引用更好的正则表达式吗?

PS:我也可以考虑从最后遍历内容并找到索引。但是我希望regex中有一些更好的方法可以快速完成工作。

3 个答案:

答案 0 :(得分:3)

(?s).*(x|y|z)正则表达式的性能问题来自于.*模式是第一个首先捕获整个字符串的子模式,然后发生回溯以找到x,{{1 }}或y。如果没有匹配项,或者匹配项位于字符串的开头,并且字符串很大,这可能会花费很长时间。

z模式似乎要好一些:它捕获([xyz])(?=[^xyz]*$)xy并断言没有其他z,{{1 }}或x直到字符串的末尾,但由于在找到匹配项后进行了每次前瞻检查,因此它也有些资源消耗。

最快完成工作的正则表达式是

y

它匹配

  • z-字符串的开头
  • ^(?:[^xyz]*+([xyz]))+ -重复1次或更多次
    • ^-除(?:[^xyz]*+([xyz]))+[^xyz]*+x以外的0个或多个字符,所有格匹配(不允许回溯到模式中)
    • y-第1组:z([xyz])x

第1组的值和数据将属于重复组的最后一次迭代(因为所有先前的数据都将在以后的每次迭代中重写)。

答案 1 :(得分:2)

解决问题的方法很少,最好的方法取决于输入的大小和模式的复杂性:

  1. 反转输入字符串和可能的模式,这可能适用于非复杂模式。不幸的是java.util.regex不允许从右向左匹配模式。

  2. 而不是使用贪婪的量词,只需匹配模式并循环Matcher.find()直到找到最后一个出现。

  3. 使用性能更好的其他正则表达式引擎,例如RE2/J: linear time regular expression matching in Java

如果选项2对于您的情况不够有效,我建议您尝试使用RE2 / J:

  

Java的标准正则表达式包java.util.regex以及许多其他广泛使用的正则表达式包(例如PCRE,Perl和Python)都采用了回溯实现策略:当模式提供两种替代方案时,例如a|b,引擎将首先尝试匹配子模式a,如果没有匹配,它将重置输入流并尝试匹配b

     

如果此类选择被深层嵌套,则此策略需要先对输入数据进行指数传递,然后才能检测到输入是否匹配。如果输入很大,则很容易构造一个运行时间将超过Universe寿命的模式。当从不受信任的来源(例如Web应用程序的用户)接受正则表达式模式时,这会带来安全风险。

     

相反,RE2算法通过使用不确定的有限自动机,一次遍历输入数据同时探索所有匹配项。

答案 2 :(得分:1)

StringBuilder都具有reverse并且是CharSequence,因此可以进行搜索。

Pattern p = Pattern.compile("[xyz]");
StringBuilder sb = new StringBuilder(str).reverse();
Matcher m = p.matcher(sb);
return m.find() ? sb.length() - m.end() : -1;

不幸的是,逆转的代价很高。

没有正则表达式的解决方案可能更快。

(逆转正确处理了BTW代理对。)