問題描述
在web中呈現文本信息時,有時需要對一些特定的字詞進行高亮顯示。比較常見的場景是關鍵詞搜索。如下圖所示(百度搜索Neo4j in action時展現的web頁面):
高亮實例.png
可見,文本中所有出現Neo4j in action的地方都用紅色進行了高亮。查看網頁源碼可以發現Neo4j in action幾個詞的前后都打上了標簽,詞前面為<em>詞后為</em>。
標簽實例.png
本文不去深究該標簽的含義,而是去探討如何方便給字符串中特定的子串前后打上如上的標簽。
解決方法
若要進行高亮,首先要找到需要高亮子串的位置。這個可以容易的通過JAVA正則表達式實現。其可以得到每個需要高亮的子串在文本中的起始(需要加入<em>)和終止(需要加入</em>)位置。這里不做贅述。
再獲得了每個需要加入<em>和</em>的位置后,可以將這些位置從小到大排列成一個List。這個List有兩個特點:
- List的大小一定是偶數,因為開始位置和結束位置總是成對出現
- List的下標為偶數(下標從0開始)時為開始位置,List的下標為奇數時為結束位置
小問題
由于我們需要向原有字符串中插入標簽,如果我們從List的第一個位置開始插入標簽,則后續的下標需要進行平移調整。舉例說明:
字符串:abcad
高亮的子串: a
List:0,1,3,4
若首先在0的位置插入<em>,則字符串變為<em>abcad,下一個插入</em>的位置應該為1+"<em>".length=1+4=5
雖然進行恰當的平移可以解決問題,但無疑增加了程序的復雜度。
小技巧
一個更聰明的做法是從List的尾部開始加入標簽,因為從尾部插入標簽后,并不會影響到其它標簽插入的下標。示例代碼如下
String text = "abcad";
String keyword = "a";
Matcher matcher = Pattern.compile(keyword).matcher(text);
List<Integer> list = new ArrayList<>();
while (matcher.find()) {
list.add(matcher.start());
list.add(matcher.end());
}
StringBuilder sb = new StringBuilder(text);
for (int i = list.size() - 1; i >= 0; i--) {
if (i % 2 == 0) {
sb.insert(list.get(i), "<em>");
} else {
sb.insert(list.get(i), "</em>");
}
}
return sb.toString();