在Java中执行大量字符串替换的最快方法

时间:2010-11-26 11:56:08

标签: java regex string

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

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方法与

一起使用
  

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

您是否知道以快速方式快速完成大量字符串替换?你对这个问题有什么建议吗?

感谢。

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

4 个答案:

答案 0 :(得分:12)

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

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

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

答案 1 :(得分:6)

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

答案 2 :(得分:4)

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

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

答案 3 :(得分:3)

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

如果您的文件不是HTML,但只包含一些HTML令牌,则有几种方法可以加快速度。

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

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

其次,考虑正则表达式:

Pattern p = Pattern.compile( your_regex );

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

如果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();

这可能有一些错误:)

优点是你必须只迭代输入一次。最大的缺点是它并不容易理解。你也可以编写一个状态机,分析每个字符应该是什么新状态,这可能会更快,甚至更多的工作。