代码之家  ›  专栏  ›  技术社区  ›  Averroes

在Java中执行许多字符串替换的最快方法

  •  11
  • Averroes  · 技术社区  · 14 年前

    我必须编写某种解析器来获取字符串并用其他字符替换某些字符集。代码如下所示:

    noHTMLString = noHTMLString.replaceAll("</p>", "\n");
    noHTMLString = noHTMLString.replaceAll("<br/>", "\n\n");
    noHTMLString = noHTMLString.replaceAll("<br />", "\n\n");
    //here goes A LOT of lines like these ones
    

    这个函数很长,执行了很多字符串替换。这里的问题是它需要很多时间,因为它调用的方法很多次,从而降低了应用程序的性能。

    我在这里读过一些关于使用StringBuilder作为替代方法的线程,但是它缺少ReplaceAll方法,正如这里所提到的 Does string.replaceAll() performance suffer from string immutability? String类中的replaceAll方法与

    Match Pattern&Matcher和Matcher.replaceAll()使用StringBuilder存储最终返回的值 所以我不知道切换到StringBuilder是否真的会减少执行替换的时间。

    你知道用快速的方法替换很多字符串吗?你对这个问题有什么建议吗?

    谢谢。

    编辑 :我必须创建一个报告,其中包含几个带有html文本的字段。对于每一行,我调用的方法将替换这些字符串中的所有html标记和特殊字符。对于完整的报告,解析所有文本需要3分钟以上的时间。问题是我必须经常调用这个方法

    4 回复  |  直到 7 年前
        1
  •  12
  •   MatBanik    14 年前

    我发现org.apache.commons.lang.StringUtils是最快的,如果你不想使用StringBuffer的话。

    你可以这样使用它:
    noHTMLString = StringUtils.replace(noHTMLString, "</p>", "\n");

    我做了性能测试,它比我定制的StrinBuffer解决方案更糟糕,类似于@extraneon提出的解决方案。

        2
  •  6
  •   Martijn Verburg    14 年前

    看起来你在那里解析HTML,你有没有想过使用 3rd party library 而不是重新发明轮子?

        3
  •  4
  •   Allanrbo    14 年前

    我同意Martijn使用一个现成的解决方案,而不是自己解析它-javax.xml包中有很多东西内置到Java中。一个好的解决方案是使用XSLT转换来替换,这看起来是一个理想的用例。然而,这是复杂的。

    为了回答这个问题,你考虑过使用 regular expression libraries ? 看起来您有许多不同的东西要匹配,并替换为相同的东西(\n或空字符串)。使用正则表达式可以是 "<br>|<br/>|<br />" 或者更聪明的像 <br.*?>" 创建一个匹配器对象,在该对象上可以调用replaceAll。

        4
  •  3
  •   extraneon    14 年前

    我完全同意马蒂金的观点。为工作选择合适的工具。

    但是,如果您的文件不是HTML,而是只包含一些HTML标记,那么有几种方法可以加快速度。

    首先,如果某些输入量不包含可替换元素,请考虑从以下内容开始:

    if (!input.contains('<')) {
        return input;
    }
    

    其次,考虑regex:

    Pattern p = Pattern.compile( your_regex );
    

    不要为每一行replaceAll创建一个模式,而是尝试将它们组合起来(regex有一个OR运算符),让模式优化regex。使用 编译的 模式,而不是在每次调用中编译它,它相当昂贵。

    如果regex有点复杂,您也可以自己实现一些更快(但可能不太可读)的替换引擎:

    StringBuilder result = new StringBuilder(input.length();
    for (int i=0; i < input.length(); i++) {
      char c = input.charAt(i);
    
      if ( c != '<' ) {
        continue;
      }
    
      int closePos = input.indexOf( '>', i);
      if (closePos == -1) {// not found
        result.append( input.substring(i, input.length());
        return result.toString();
      }
      i = closePos;
      String token = input.substring(i, closePos);
      if ( token.equals( "p/" ) {
        result.append("\\n");
      } else if (token.equals(...)) {
      } else if (...) {
      } 
    }
    return result.toString();
    

    这可能有一些错误:)

    这样做的好处是您只需遍历输入一次。最大的缺点是它不太容易理解。你也可以写一个状态机,分析每个字符的新状态应该是什么,这可能会更快,甚至更多的工作。