js实现敏感词过滤算法
最近弄了一個(gè)用戶發(fā)表評論的功能,用戶上傳了評論,再文章下可以看到自己的評論,但作為社會主義接班人,踐行社會主義核心價(jià)值觀,所以給評論敏感詞過濾的功能不可少,在網(wǎng)上找了資料,發(fā)現(xiàn)已經(jīng)有非常成熟的解決方案。
常用的方案用這么兩種
全文搜索,逐個(gè)匹配。這種聽起來就不夠高大上,在數(shù)據(jù)量大的情況下,會有效率問題,文末有比較
DFA算法-確定有限狀態(tài)自動(dòng)機(jī)
DFA算法介紹
DFA是一種計(jì)算模型,數(shù)據(jù)源是一個(gè)有限個(gè)集合,通過當(dāng)前狀態(tài)和事件來確定下一個(gè)狀態(tài),即 狀態(tài)+事件=下一狀態(tài),由此逐步構(gòu)建一個(gè)有向圖,其中的節(jié)點(diǎn)就是狀態(tài),所以在DFA算法中只有查找和判斷,沒有復(fù)雜的計(jì)算,從而提高算法效率
實(shí)現(xiàn)邏輯
構(gòu)造數(shù)據(jù)結(jié)構(gòu)
將敏感詞轉(zhuǎn)換成樹結(jié)構(gòu),舉例敏感詞有著這么幾個(gè) ['日本鬼子','日本人','日本男人'],那么數(shù)據(jù)結(jié)構(gòu)如下(圖片引用參考文章)
數(shù)據(jù)結(jié)構(gòu)每個(gè)文字是一個(gè)節(jié)點(diǎn),連續(xù)的節(jié)點(diǎn)組成一個(gè)詞,日本人對應(yīng)的就是中間的那條鏈,我們可以使用對象或者map來構(gòu)建樹,這里的栗子采用map構(gòu)建節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)中有個(gè)狀態(tài)標(biāo)識,用來表示當(dāng)前節(jié)點(diǎn)是不是最后一個(gè),每條鏈路必須要有個(gè)終點(diǎn)節(jié)點(diǎn),先來看下構(gòu)建節(jié)點(diǎn)的流程圖
流程圖判斷邏輯
先從文本的第一個(gè)字開始檢查,比如你我是日本鬼子,第一個(gè)字 你,在樹的第一層找不到這個(gè)節(jié)點(diǎn),那么繼續(xù)找第二個(gè)字,到了日的時(shí)候,第一層節(jié)點(diǎn)找到了,那么接著下一層節(jié)點(diǎn)中查找本,同時(shí)判斷這個(gè)節(jié)點(diǎn)是不是結(jié)尾節(jié)點(diǎn),若是結(jié)尾節(jié)點(diǎn),則匹配成功了,反之繼續(xù)匹配。
代碼實(shí)現(xiàn)
####構(gòu)造數(shù)據(jù)結(jié)構(gòu)
/** * @description * 構(gòu)造敏感詞map * @private * @returns */ private makeSensitiveMap(sensitiveWordList) {// 構(gòu)造根節(jié)點(diǎn)const result = new Map();for (const word of sensitiveWordList) {let map = result;for (let i = 0; i < word.length; i++) {// 依次獲取字const char = word.charAt(i);// 判斷是否存在if (map.get(char)) {// 獲取下一層節(jié)點(diǎn)map = map.get(char);} else {// 將當(dāng)前節(jié)點(diǎn)設(shè)置為非結(jié)尾節(jié)點(diǎn)if (map.get('laster') === true) {map.set('laster', false);}const item = new Map();// 新增節(jié)點(diǎn)默認(rèn)為結(jié)尾節(jié)點(diǎn)item.set('laster', true);map.set(char, item);map = map.get(char);}}}return result; }最終map結(jié)構(gòu)如下
結(jié)構(gòu)查找敏感詞
/** * @description * 檢查敏感詞是否存在 * @private * @param {any} txt * @param {any} index * @returns */ private checkSensitiveWord(sensitiveMap, txt, index) {let currentMap = sensitiveMap;let flag = false;let wordNum = 0;//記錄過濾let sensitiveWord = ''; //記錄過濾出來的敏感詞for (let i = index; i < txt.length; i++) {const word = txt.charAt(i);currentMap = currentMap.get(word);if (currentMap) {wordNum++;sensitiveWord += word;if (currentMap.get('laster') === true) {// 表示已到詞的結(jié)尾flag = true;break;}} else {break;}}// 兩字成詞if (wordNum < 2) {flag = false;}return { flag, sensitiveWord }; } /** * @description * 判斷文本中是否存在敏感詞 * @param {any} txt * @returns */ public filterSensitiveWord(txt, sensitiveMap) {let matchResult = { flag: false, sensitiveWord: '' };// 過濾掉除了中文、英文、數(shù)字之外的const txtTrim = txt.replace(/[^\u4e00-\u9fa5\u0030-\u0039\u0061-\u007a\u0041-\u005a]+/g, '');for (let i = 0; i < txtTrim.length; i++) {matchResult = checkSensitiveWord(sensitiveMap, txtTrim, i);if (matchResult.flag) {console.log(`sensitiveWord:${matchResult.sensitiveWord}`);break;}}return matchResult; }效率
為了看出DFA的效率,我做了個(gè)簡單的小測試,測試的文本長度為5095個(gè)漢字,敏感詞詞庫中有2000個(gè)敏感詞,比較的算法分別為 DFA算法 和 String原生對象提供的 indexOfAPI做比較
// 簡單的字符串匹配-indexOf ensitiveWords.forEach((word) => {if (ss.indexOf(word) !== -1) {console.log(word)} })分別將兩個(gè)算法執(zhí)行100次,得到如下結(jié)果
比較結(jié)果可直觀看出,DFA的平均耗時(shí)是在1ms左右,最大為5ms;indexOf方式的平均耗時(shí)在9ms左右,最大為14ms,所以DFA效率上還是非常明顯有優(yōu)勢的。
參考文章
https://zh.wikipedia.org/wiki/%E7%A1%AE%E5%AE%9A%E6%9C%89%E9%99%90%E7%8A%B6%E6%80%81%E8%87%AA%E5%8A%A8%E6%9C%BA
https://blog.csdn.net/chenssy/article/details/26961957
總結(jié)
以上是生活随笔為你收集整理的js实现敏感词过滤算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [引]VS2005 之 Visual B
- 下一篇: 3.Cood