1、過濾敏感詞
Spring Boot實踐,開發社區核心功能
完成過濾敏感詞
Trie
- 名稱:Trie也叫做字典樹、前綴樹(Prefix Tree)、單詞查找樹
- 特點:查找效率高,消耗內存大
- 應用:字符串檢索、詞頻統計、字符串排序等
Trie 搜索字符串的效率主要跟字符串的長度有關
最大的特點就是共享字符串的公共前綴來達到節省空間的目的了
更多Trie 相關的數據結構和算法
Double-array Trie、Suffix Tree、Patricia Tree、Crit-bit Tree、AC自動機
實現敏感詞過濾器
- 定義前綴樹
- 根據敏感詞,初始化前綴樹
- 編寫過濾敏感詞的方法
SensitiveFilter.java
@Component
public class SensitiveFilter {
private static final Logger logger = LoggerFactory.getLogger(SensitiveFilter.class);
// 替換符
private static final String REPLACEMENT = "***";
// 根節點
private TrieNode rootNode = new TrieNode();
//PostConstruct 容器實例化Bean 構造器 服務初始化
@PostConstruct
public void init() {
try (
InputStream is = this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
) {
String keyword;
while ((keyword = reader.readLine()) != null) {
// 添加到前綴樹
this.addKeyword(keyword);
}
} catch (IOException e) {
logger.error("加載敏感詞文件失敗: " + e.getMessage());
}
}
// 將一個敏感詞添加到前綴樹中
private void addKeyword(String keyword) {
TrieNode tempNode = rootNode;
for (int i = 0; i < keyword.length(); i++) {
char c = keyword.charAt(i);
TrieNode subNode = tempNode.getSubNode(c);
if (subNode == null) {
// 初始化子節點
subNode = new TrieNode();
tempNode.addSubNode(c, subNode);
}
// 指向子節點,進入下一輪循環
tempNode = subNode;
// 設置結束標識
if (i == keyword.length() - 1) {
tempNode.setKeywordEnd(true);
}
}
}
/**
* 過濾敏感詞
*
* @param text 待過濾的文本
* @return 過濾後的文本
*/
public String filter(String text) {
if (StringUtils.isBlank(text)) {
return null;
}
// 指針1
TrieNode tempNode = rootNode;
// 指針2
int begin = 0;
// 指針3
int position = 0;
// 結果
StringBuilder sb = new StringBuilder();
while (position < text.length()) {
char c = text.charAt(position);
// 跳過符號
if (isSymbol(c)) {
// 若指針1處於根節點,將此符號計入結果,讓指針2向下走一步
if (tempNode == rootNode) {
sb.append(c);
begin++;
}
// 無論符號在開頭或中間,指針3都向下走一步
position++;
continue;
}
// 檢查下級節點
tempNode = tempNode.getSubNode(c);
if (tempNode == null) {
// 以begin開頭的字符串不是敏感詞
sb.append(text.charAt(begin));
// 進入下一個位置
position = ++begin;
// 重新指向根節點
tempNode = rootNode;
} else if (tempNode.isKeywordEnd()) {
// 發現敏感詞,將begin~position字符串替換掉
sb.append(REPLACEMENT);
// 進入下一個位置
begin = ++position;
// 重新指向根節點
tempNode = rootNode;
} else {
// 檢查下一個字符
position++;
}
}
// 將最後一批字符計入結果
sb.append(text.substring(begin));
return sb.toString();
}
// 判斷是否爲符號
private boolean isSymbol(Character c) {
// 0x2E80~0x9FFF 是東亞文字範圍
return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
}
// 前綴樹
private class TrieNode {
// 關鍵詞結束標識
private boolean isKeywordEnd = false;
// 子節點(key是下級字符,value是下級節點)
private Map<Character, TrieNode> subNodes = new HashMap<>();
public boolean isKeywordEnd() {
return isKeywordEnd;
}
public void setKeywordEnd(boolean keywordEnd) {
isKeywordEnd = keywordEnd;
}
// 添加子節點
public void addSubNode(Character c, TrieNode node) {
subNodes.put(c, node);
}
// 獲取子節點
public TrieNode getSubNode(Character c) {
return subNodes.get(c);
}
}
}
要過濾的單詞sensitive-words.txt
shit
傻逼
笨蛋
...
敏感詞
測試
SensitiveTests.java
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class SensitiveTests {
@Autowired
private SensitiveFilter sensitiveFilter;
@Test
public void testSensitiveFilter() {
//這是發的人比較初級的
String text = "I'm a piece of shit,我就是傻逼呀,我個笨蛋," + "留下不學無術的眼淚!!!!";
text = sensitiveFilter.filter(text);
System.out.println(text);//I'm a piece of ***,我就是***呀,我個***,留下不學無術的眼淚!!!!
text = "I'm a piece of ☆sh☆it,我就是☆傻☆☆逼☆呀,@我個☆笨☆蛋," + "留下不學無術的眼淚!!!";
text = sensitiveFilter.filter(text);
System.out.println(text);//I'm a piece of ☆***,我就是☆***☆呀,@我個☆***,留下不學無術的眼淚!!!
}
}
Result
記錄
1、高薪求職項目課 - vol.7 - https://www.nowcoder.com/courses/semester/senior
是記錄這個社區項目的筆記。
Github : https://github.com/liuawen/play-community