将替换正则表达式转换为java算法

时间:2011-01-23 16:13:40

标签: java regex algorithm performance

我有以下java正则表达式替换逻辑

text.replaceAll("(?i)(" + keyword + ")(?!([^<]+)?>>)", "<b>$1</b>");

它需要keyword并在HTML页面上查找它,同时忽略HTML标记的大小写和内容。比它捕获找到的关键字并用<b></b>标签围绕它。

如何使用StringBuilderStringBuffer,可能HashMap来执行此操作?目标是提高绩效。

更新

我使用新的commons lang 3 beta package创建了以下方法:

public static String highlight(String text, String q) {
    String[] textAr = StringUtils.split(text, " ");
    int len = textAr.length;
    int index = 0;
    while (index < len){
         if (textAr[index].startsWith("<")) {
            while (!textAr[index].endsWith(">")) {
                index++;
            }
         }
         if (StringUtils.equalsIgnoreCase(textAr[index], q)){

             textAr[index] = "<b>"+textAr[index]+"</b>";
         }
         index++;
    }
    return StringUtils.join(textAr," ");
}

经过几次测试后,我从上述解决方案中获得了约10%的性能提升。任何关于如何在没有正则表达式的情况下做得更好的建议将不胜感激。

5 个答案:

答案 0 :(得分:1)

您可能希望在以下情况下转义关键字:

Pattern p = text.replaceAll("(?i)(" + Pattern.quote(keyword) + ")(?!([^<]+)?>>)", "<b>$1</b>");

然后你需要创建一个匹配器

Matcher m = p.matcher(myInputString);

如果输入不匹配,那么你就完成了:

if (!m.find()) { return myInputString; }

否则分配输出缓冲区:

StringBuilder out = new StringBuilder(myInputString.length() + 16);

并标记所有出现的关键字bold:

int nCharsProcessed = 0;
do {
  out.append(myInputString, nCharsProcessed, m.start(1))
     .append("<b>")
     .append(m.group(1))
     .append("</b>");
  nCharsProcessed = m.end(1);
} while (m.find());

最后,在最后一次匹配后连接部分并返回

out.append(myInputString, nCharsProcessed, myInputString.length());
return out.toString();

答案 1 :(得分:1)

无论如何,

replaceAll已经可以使用StringBuffers了。 (嗯,确切地说,Matcher.replaceAll()使用StringBuffer,但String.replaceAll只委托给Matcher.replaceAll())

为了获得更好的性能,您可以使用StringBuffer构建正则表达式字符串:

    String head = "(?i)(";
    String tail = ")(?!([^<]+)?>>)";

    StringBuffer regex = new StringBuffer();
    regex.append(head);
    regex.append(keyword);
    regex.append(tail);

    text.replaceAll(regex.toString(), "<b>$1</b>");

我真的不知道,如果有一个比Matcher类更快的替换实现。但是在你使用StringBuffer自己实现之前,我想告诉你,它已经以这种方式实现了。

以下伪代码可能有问题,但你可以这样试试。 (不能保证更好的性能,但如果没有正则表达式,这应该与上面相同)

StringBuffer sb = new StringBuffer(text);

int i = 0;
int size = text.size()
while(i<size) {
    if(sb.charAt(i) == '<') {
        increase i until you find '>';
    }
    if(sb.charAt(i) == keyword.charAt(0) {
        if(next chars of sb match next chars of keyword) {
            insert "<b>" before and "</b>" after the keyword;
            size += 7;
            i += keyword.size() + 7;
        }
    }
}

你可能还想看一下replaceAll的Matcher实现:http://kickjava.com/src/java/util/regex/Matcher.java.htm

答案 2 :(得分:1)

虽然我同意Nikita:解析HTML的最佳方法是使用HTML或XML解析器。

但如果你真的需要这个,这里有一些提示。

  1. 字符串缓冲区是字符串构建器的线程安全版本,因此如果您不必是线程安全的,或者其他层使用字符串构建器解决了线程安全问题。
  2. StringBuilder不支持使用Patterns替换。字符串支持。但是当关键字数量很高时直接使用字符串是无效的。
  3. 因此,最有效的方法是生成包含所有关键字的模式,然后执行一次替换操作。例如,如果你有关键字foo,bar,tar,就像创建正则表达式一样 regex = (?i)(foo|bar|tar)(?!([^<]+)?>>)
  4. 现在运行text.replaceAll(regex);

    您可以在创建正则表达式时使用StringBuilder,但我建议您使用 来自jakarta utils的StringUtils.join()或来自Guava的类似实用程序。

答案 3 :(得分:1)

在关键字上拆分然后在StringBuffer中连接所有内容

import java.io.*;
import java.util.*;


class Hilighter {

        public static String regex(String text, String key) {
                System.out.println(System.currentTimeMillis());
                text = text.replaceAll("(?i)(" + key + ")(?!([^<]+)?>>)", "<b>$1</b>");
                System.out.println(System.currentTimeMillis());
                return text;
        }


        public static String splitr(String text, String key) {
                System.out.println(System.currentTimeMillis());
                String[] parts = text.split(key);
                StringBuffer buffer = new StringBuffer();
                buffer.append(parts[0]);
                for (int i = 1; i < parts.length; i++) {
                        buffer.append("<b>");
                        buffer.append(key);
                        buffer.append("</b>");
                        buffer.append(parts[i]);
                }
                System.out.println(System.currentTimeMillis());
                return buffer.toString();
        }


        public static void main(String[] args) {
                try {
                        String text = readFileAsString("./test.html");
                        text = splitr(text, args[0]);
                        text = regex(text, args[0]);
                } catch (Exception e) {
                        System.err.println("IO ERROR");
                }
        }


        private static String readFileAsString(String filePath) throws java.io.IOException{
                StringBuffer fileData = new StringBuffer(1000);
                BufferedReader reader = new BufferedReader(new FileReader(filePath));
                char[] buf = new char[1024];
                int numRead=0;
                while((numRead=reader.read(buf)) != -1){
                    String readData = String.valueOf(buf, 0, numRead);
                    fileData.append(readData);
                    buf = new char[1024];
                }
                reader.close();
                return fileData.toString();
        }



}


答案 4 :(得分:1)

请注意,split()也使用正则表达式。如果你真的需要与正则表达式无关的东西,那么你就自己循环遍历字符串。或者使用indexOf()查找第一个匹配项,然后查看它是否后跟一个小于号。

我认为你并不认为正则表达式不能直接使用。我认为你的意思是不应该直接使用模式。