Превращение заменить регулярное выражение в алгоритм Java - PullRequest
0 голосов
/ 23 января 2011

У меня есть следующая Java регулярное выражение заменить логику

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

Что он делает, он берет keyword и ищет его на странице HTML, игнорируя регистр и содержимое тегов HTML. Затем он захватывает найденное ключевое слово и окружает его тегами <b></b>.

Как мне это сделать с использованием StringBuilder или StringBuffer, возможно HashMap? Цель - повышение производительности.

UPDATE

Я описал следующий метод, используя новый бета-пакет commons lang 3 :

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% по сравнению с вышеуказанным решением. Буду признателен за любые предложения о том, как сделать его лучше, БЕЗ Regex.

Ответы [ 5 ]

1 голос
/ 23 января 2011

Обратите внимание, что split () также использует регулярные выражения.Если вам действительно нужно что-то, что не имеет ничего общего с регулярными выражениями, вам придется самим проходить через строку.Или используя indexOf (), чтобы найти первое совпадение, а затем посмотреть, сопровождается ли оно знаком «меньше».

Не думаю, что вы имеете в виду, что регулярные выражения нельзя использовать буквально.Я думаю, вы имеете в виду, что шаблоны не должны использоваться напрямую.

1 голос
/ 23 января 2011

Разделить по ключевому слову, затем объединить все в 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();
        }



}


1 голос
/ 23 января 2011

replaceAll все равно уже работает со StringBuffers.(Если быть точным, Matcher.replaceAll () использует StringBuffer, но String.replaceAll делегирует только Matcher.replaceAll ())

Для повышения производительности вы можете создать регулярное выражение String с помощью 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;
        }
    }
}

Вы также можете взглянуть на реализацию Matcher replaceAll: http://kickjava.com/src/java/util/regex/Matcher.java.htm

1 голос
/ 23 января 2011

Хотя я согласен с Никитой: лучший способ разбора HTML - это использование анализатора HTML или XML.

Но если вам это действительно нужно, вот несколько советов.

  1. Строковый буфер является поточно-ориентированной версией построителя строк, поэтому, если вам не нужно быть поточно-ориентированным или если проблемы с безопасностью потока решаются другим слоем, используйте построитель строк.
  2. StringBuilder не поддерживает замену с использованием шаблонов.Строки поддерживают.Но работать напрямую со строками при большом количестве ключевых слов неэффективно.
  3. Таким образом, наиболее эффективный способ - создать шаблон, содержащий все ключевые слова, и затем выполнить операцию замены один раз.Например, если у вас есть ключевые слова foo, bar, tar, создайте регулярное выражение, например regex = (?i)(foo|bar|tar)(?!([^<]+)?>>)

Теперь запустите text.replaceAll(regex);

Вы можете использовать StringBuilder при создании регулярного выражения, но я бырекомендуем использовать StringUtils.join() из утилит jakarta или аналогичных утилит из Guava.

1 голос
/ 23 января 2011

Вы, вероятно, хотите экранировать ключевое слово на всякий случай:

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);

и отметьте все вхождения ключевого слова жирным шрифтом:

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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...