Как мне избежать "+" в сопоставлении с образцом, чтобы выделить ключевое слово? - PullRequest
0 голосов
/ 13 июля 2011

Я реализую подсветку ключевых слов в Java. Я использую java.util.regex.Pattern для выделения (выделение жирным шрифтом) ключевого слова в строковом контенте. Следующий фрагмент кода работает нормально для буквенно-цифровых ключевых слов, но не работает для некоторых специальных символов. Например, в строковом контенте я хотел бы выделить ключевое слово c++, которое имеет специальный символ + (плюс), но оно не выделяется должным образом. Как мне выбрать + символ, чтобы выделить c++?

public static void main(String[] args)
{
    String content = "java,c++,ejb,struts,j2ee,hibernate";
    System.out.println("CONTENT: " + content);
    String highlight = "C++";

    System.out.println("HIGHLIGHT KEYWORD: " + highlight);

    //highlight = highlight.replaceAll(Pattern.quote("+"), "\\\\+");
    java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\b" + highlight + "\\b", java.util.regex.Pattern.CASE_INSENSITIVE);
    System.out.println("PATTERN: " + pattern.pattern());
    java.util.regex.Matcher matcher = pattern.matcher(content);

    while (matcher.find()) {
        System.out.println("Match found!!!");
        for (int i = 0; i <= matcher.groupCount(); i++) {
        System.out.println(matcher.group(i));
        content = matcher.replaceAll("<B>" + matcher.group(i) + "</B>");
        }
    }
    System.out.println("RESULT: " + content);
}

Выход:
СОДЕРЖАНИЕ: Java, C ++, EJB, Struts, J2ee, Hibernate
Ключевое слово: C ++
ШАБЛОН: \ bC ++ \ b
Совпадение найдено !!!
с
РЕЗУЛЬТАТ: java, c ++, ejb, стойки, j2ee, hibernate


Я даже пытался уйти от '+' до вызова Pattern.compile , как это,
highlight = highlight.replaceAll(Pattern.quote("+"), "\\\\+");

но я все еще не могу понять синтаксис правильно. Может кто-нибудь помочь мне решить это?

Ответы [ 4 ]

6 голосов
/ 13 июля 2011

Это должно сделать то, что вам нужно:

Pattern pattern = Pattern.compile(
    "\\b" 
    + Pattern.quote(highlight)
    + "\\b",
    Pattern.CASE_INSENSITIVE);

Обновление: вы правы, вышеприведенное не работает для C ++ (\b соответствует границам слова и не распознает ++ как слово). Нам нужно более сложное решение:

Pattern pattern = Pattern.compile(
    "\\b" 
    + Pattern.quote(highlight)
    + "(?![^\\p{Punct}\\s])", // matches if the match is not followed by
                              // anything other than whitespace or punctuation
    Pattern.CASE_INSENSITIVE);

Обновление в ответ на комментарии: кажется, вам нужно больше логики при создании шаблона. Вот вспомогательный метод для создания шаблона для вас:

private static final String WORD_BOUNDARY = "\\b";
// edit this to suit your neds:
private static final String ALLOWED = "[^,.!\\-\\s]";
private static final String LOOKAHEAD = "(?!" + ALLOWED + ")";
private static final String LOOKBEHIND = "(?<!" + ALLOWED + ")";

public static Pattern createHighlightPattern(final String highlight) {
    final Pattern pattern = Pattern.compile(
            (Character.isLetterOrDigit(highlight.charAt(0)) 
             ? WORD_BOUNDARY : LOOKBEHIND)
            + Pattern.quote(highlight)
            + (Character.isLetterOrDigit(highlight.charAt(highlight.length() - 1))
             ? WORD_BOUNDARY : LOOKAHEAD),
            Pattern.CASE_INSENSITIVE);
    return pattern;
}

А вот тестовый код для проверки его работоспособности:

private static void testMatch(final String haystack, final String needle) {
    final Matcher matcher = createHighlightPattern(needle).matcher(haystack);
    if (!matcher.find())
        System.out.println("Failed to find pattern " + needle);
    while (matcher.find())
        System.out.println("Found additional match: " + matcher.group() +
                           " for pattern " + needle);
}

public static void main(final String[] args) {
    final String testString = "java,c++,hibernate,.net,asp.net,c#,spring";
    testMatch(testString, "java");
    testMatch(testString, "c++");
    testMatch(testString, ".net");
    testMatch(testString, "c#");
}

Когда я запускаю этот метод, я не вижу никакого вывода (что хорошо: -))

1 голос
/ 13 июля 2011

Проблема в том, что \b привязка границы слова не совпадает, потому что + - это символ, не являющийся словом, и я предполагаю, что после него есть пробел, который также не является словом.

Граница слова \b соответствует изменению от символа слова (член в \w) к несловесному символу (нет члена \w).

Также, если вы хотите сопоставить + буквально, вы должны избежать его. Здесь вы ищете C++, что означает совпадение по крайней мере с одним C, а ++ является собственническим квантификатором, совпадающим по крайней мере с 1 C и не возвращающим.

Попробуйте изменить свой рисунок на что-то вроде этого

java.util.regex.Pattern.compile("\\b" + highlight + "(?=\s)", java.util.regex.Pattern.CASE_INSENSITIVE);

(?=\s) - это позитивный прогноз, который проверит, есть ли пробелы после вашего highlight

Кроме того, вам понадобится закрыть значок +, который вы ищете.

0 голосов
/ 15 июля 2011

Предполагая, что ваше ключевое слово не начинается и не заканчивается пунктуацией, здесь приведено прокомментированное регулярное выражение, которое использует lookahead и lookbehind для достижения желаемого поведения соответствия:

// Compile regex to match a keyword or keyphrase.
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(
    "(?<=[\\s'\".?!,;:]|^)  # Word preceded by ws, quote, punct or BOS.\n" +

    // Escape any regex metacharacters in the keyword phrase.
    java.util.regex.Pattern.quote(highlight) + " # Keyword to be matched.\n" +

    "(?=[\\s'\".?!,;:]|$)   # Word followed by ws, quote, punct or EOS.", 
    Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.COMMENTS);

Обратите внимание, что это решение работает, даже если ваше ключевое словофраза, содержащая пробелы.

0 голосов
/ 13 июля 2011

Все, что вам нужно, это здесь:

Pattern.compile("\\Q"+highlight+"\\E", java.util.regex.Pattern.CASE_INSENSITIVE);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...