【问题标题】:Optimizing several RegEx in Java Code在 Java 代码中优化几个 RegEx
【发布时间】:2012-01-05 10:32:18
【问题描述】:

下面提到的 RegEx 在非常大的字符串或超过 2000 行的情况下表现很差。基本上,Java 字符串由 PL/SQL 脚本组成。

1- 用字符前后的空格替换每次出现的定界字符,例如 ||、!= 或 > 符号。这需要无限的时间并且永远不会结束,因此无法记录时间。

// Delimiting characters for SQLPlus
private static final String[] delimiters = { "\\|\\|", "=>", ":=", "!=", "<>", "<", ">", "\\(", "\\)", "!", ",", "\\+", "-", "=", "\\*", "\\|" };


for (int i = 0; i < delimiters.length; i++) {
    script = script.replaceAll(delimiters[i], " " + delimiters[i] + " ");
}

2- 以下模式查找所有出现的正斜杠 / 除了前面带有 * 的那些。这意味着不要在块注释语法中寻找正斜杠。 2000 行字符串大约需要 103 秒。

Pattern p = Pattern.compile("([^\\*])([\\/])([^\\*])");
Matcher m = p.matcher(script);
while (m.find()) {
    script = script.replaceAll(m.group(2), " " + m.group(2) + " ");
}

3- 从日期或日期格式中删除任何空格

Pattern p = Pattern.compile("(?i)(\\w{1,2}) +/ +(\\w{1,2}) +/ +(\\w{2,4})");
// Create a matcher with an input string
Matcher m = p.matcher(script);
while (m.find()) {
    part1 = script.substring(0, m.start());
    part2 = script.substring(m.end());
    script = part1 + m.group().replaceAll("[ \t]+", "") + part2;
    m = p.matcher(script);
}

有什么方法可以优化所有三个 RegEx 以减少它们花费的时间?

谢谢

阿里

【问题讨论】:

  • 你应该把这个问题分成三个独立的问题。这也将帮助您创建比“优化多个正则表达式”更有意义(因此也很有趣)的问题标题...

标签: java regex optimization


【解决方案1】:

导致性能问题的不是正则表达式,而是您对文本进行了多次遍历,并不断创建新的 Pattern 对象。正如蒂姆所指出的,受到影响的不仅仅是性能;当你这样做时,很容易弄乱先前通过的结果。

事实上,我猜日期中那些多余的空格只是你的其他替代品的副作用。如果是这样,这里有一种方法可以一次性完成所有替换,而无需添加不需要的字符:

static String doReplace(String input)
{
  String regex = 
      "/\\*[^*]*(?:\\*(?!/)[^*]*)*\\*/|"      // a comment
    + "\\b\\d{2}/\\d{2}/\\d{2,4}\\b|"         // a date
    + "(/|\\|\\||=>|[:!]=|<>|[<>()!,+=*|-])"; // an operator

  Matcher m = Pattern.compile(regex).matcher(input);
  StringBuffer sb = new StringBuffer();
  while (m.find())
  {
     // if we found an operator, replace it
    if (m.start(1) != -1)
    {
      m.appendReplacement(sb, " $1 ");
    }
  }
  m.appendTail(sb);
  return sb.toString();
}

see the online demo

诀窍是,如果你不调用appendReplacement(),匹配位置不会更新,所以就好像匹配没有发生一样。因为我忽略了它们,所以 cmets 和日期会与其余不匹配的文本一起重新插入,我不必担心匹配其中的斜杠字符。

编辑确保正则表达式的“注释”部分位于“运算符”部分之前。否则,每条评论的前导/ 将被视为运算符。

【讨论】:

    【解决方案2】:

    当然。您的第二种方法“几乎”很好。问题是您不使用您的模式来替换本身。当您使用str.replaceAll() 时,实际上每次调用此方法时都会创建Pattern 实例。 Pattern.compile() 为您调用,需要 90% 的时间。

    您应该改用Matcher.replaceAll()

        String script = "dfgafjd;fjfd;jfd;djf;jds\\fdfdf****\\/";
        String result = script;
    
        Pattern p = Pattern.compile("[\\*\\/\\\\]"); // write all characters you want to remove here.
        Matcher m = p.matcher(script);
        if (m.find()) {
            result = m.replaceAll("");
        }       
        System.out.println(result);
    

    【讨论】:

      【解决方案3】:

      我会回答第一个问题。

      您可以将所有这些组合成一个正则表达式替换操作:

      script = script.replaceAll("\\|\\||=>|[:!]=|<>|[<>()!,+=*|-]", " $0 ");
      

      说明:

      \|\|            # Match ||
      |               # or
      =>              # =>
      |               # or
      [:!]=           # := or !=
      |               # or
      <>              # <>
      |               # or
      [<>()!,+=*|-]   # <, >, (, ), !, comma, +, =, *, | or -
      

      【讨论】:

      • 但这会更快吗?我对此表示怀疑。
      • @FailedDev:我敢肯定它会的。最初的解决方案将采用a:=b||c,将其转换为a:=b || c,然后将其转换为a := b || c,然后转换为a : = b || c,然后转换为a : = b | | c(我只展示了实际发生的步骤,我'肯定那不是他打算发生的事情)。许多由此引入的双空格在这里没有出现,但你明白了。
      猜你喜欢
      • 1970-01-01
      • 2010-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-17
      • 1970-01-01
      相关资源
      最近更新 更多