如何在模式匹配中转义'+'以突出显示关键字?

时间:2011-07-13 09:38:15

标签: java regex escaping

我正在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 ++
PATTERN:\ bC ++ \ b
匹配发现!!!
ç
结果:java, c ++,ejb,struts,j2ee,hibernate

<小时/> 在调用 Pattern.compile 之前,我甚至试图逃避'+',

highlight = highlight.replaceAll(Pattern.quote("+"), "\\\\+");

但我仍然无法正确使用语法。有人可以帮我解决这个问题吗?

4 个答案:

答案 0 :(得分:6)

这应该做你需要的:

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 :(得分:1)

问题是\b字边界锚不匹配,因为+是非单词字符,我假设后面有一个空格,也是非单词字符。

单词边界\b匹配从单词字符(\w中的成员)到非单词字符(不是\w的成员)的更改。

此外,如果你想要匹配一个+字面上你必须逃脱它。您在此处搜索的C++表示匹配至少一个C,而++是一个至少与1 C匹配的占有量词,并且不会回溯。

尝试将您的模式更改为此类

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

(?=\s)是一个积极的先行,将检查highlight

后面是否有空格

此外,您需要提供您正在搜索的+。

答案 2 :(得分:0)

您需要的就是:

Pattern.compile("\\Q"+highlight+"\\E", java.util.regex.Pattern.CASE_INSENSITIVE);

答案 3 :(得分:0)

假设您的关键字没有以标点符号开头或结尾,这里是一个注释的正则表达式,它使用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);

请注意,即使您的关键字是包含空格的短语,此解决方案仍然有效。