【问题标题】:How to find a last occurrence of set of characters in string using regex in java?如何在java中使用正则表达式查找字符串中最后一次出现的字符集?
【发布时间】:2019-10-19 19:23:03
【问题描述】:

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

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

int findIndex(String str){
  int index = -1;
  Pattern p = Pattern.compile("(?s).*(x|y|z)");
  Matcher m = regex.matcher(str);
  if(m.find()){
    index = m.start(1);
  }
  return index;
}

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

但如果没有匹配,则需要很长时间(600000 个字符需要 17 分钟),因为它是贪婪匹配。

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

PS:我也可以考虑从最后遍历内容并找到索引。但我希望正则表达式中有更好的方法可以快速完成这项工作。

【问题讨论】:

  • 为什么不直接删除.*,然后将if 改为while
  • 我很确定三个“lastIndexOf”并且比较结果比任何 RegEx 都快
  • 试试"([xyz])(?=[^xyz]*$)"。开头使用.* 总是会导致很多回溯。
  • 使用正则表达式吗?
  • 似乎是一个解决方案。会尝试一下。谢谢@AndyTurner

标签: java regex regex-greedy


【解决方案1】:

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

  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

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

相比之下,RE2 算法使用非确定性有限自动机在输入数据的单次传递中同时探索所有匹配项。

【讨论】:

    【解决方案2】:

    (?s).*(x|y|z) 正则表达式的性能问题来自以下事实:.* 模式是首先抓取整个字符串的第一个子模式,然后发生回溯以找到 xyz。如果没有匹配,或者匹配在字符串的开头,并且字符串很大,这可能需要很长时间。

    ([xyz])(?=[^xyz]*$) 模式似乎好一点:它捕获 xyz 并断言没有其他 xyz 直到结束字符串,但由于找到匹配项后的每次前瞻检查,它也有点消耗资源。

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

    ^(?:[^xyz]*+([xyz]))+
    

    匹配

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

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

    【讨论】:

    • @user7294900 如果使用 if 块,则没有必要,但它只是明确地告诉我们我们正在从头开始搜索。
    【解决方案3】:

    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 代理对通过反转正确处理。)

    【讨论】:

      猜你喜欢
      • 2019-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多