我正在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("+"), "\\\\+");
但我仍然无法正确使用语法。有人可以帮我解决这个问题吗?
答案 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);
请注意,即使您的关键字是包含空格的短语,此解决方案仍然有效。