• JS 敏感词识别-模式匹配算法 DFA


    DFA(Deterministic Finite Automaton,即确定有穷自动机。其原理为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号,其中一个状态是初态,某些状态是终态。但不同于不确定的有限自动机,DFA中不会有从同一状态出发的两条边标志有相同的符号。

    举例有限状态机字典如下

    @startuml
    (王) -> (八)
    (八) -> (蛋)
    (八) -> (羔)
    (羔) -> (子)
    @enduml
    
    {
      '王': {
        '八': {
          '蛋': {
            empty: true
          }
        }
        '羔': {
          '子': {
            empty: true
          }
        }
      }
    }
    

    输入为王八端时,逐字对比有限状态机的字典

    1. 王字命中
    2. 八字命中
    3. 端没有命中,则返回八字继续开始匹配
    4. 八字没有命中有限状态机,over

    输入为王八蛋时

    1. 王字命中
    2. 八字命中
    3. 蛋命中
    4. 下一个 empty 为 true 命中,匹配成功。

    实现

    找出敏感词代码

    'use strict';
    
    // 特殊符号过滤逻辑
    const ignoreChars = " 	
    ~!@#$%^&*()_+-=【】、{}|;':",。、《》?αβγδεζηθικλμνξοπρστυφχψωΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ。,、;:?!…—·ˉ¨‘’“”々~‖∶"'`|〃〔〕〈〉《》「」『』.〖〗【】()[]{}ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩①②③④⑤⑥⑦⑧⑨⑩⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇≈≡≠=≤≥<>≮≯∷±+-×÷/∫∮∝∞∧∨∑∏∪∩∈∵∴⊥∥∠⌒⊙≌∽√§№☆★○●◎◇◆□℃‰€■△▲※→←↑↓〓¤°#&@\︿_ ̄―♂♀┌┍┎┐┑┒┓─┄┈├┝┞┟┠┡┢┣│┆┊┬┭┮┯┰┱┲┳┼┽┾┿╀╁╂╃└┕┖┗┘┙┚┛━┅┉┤┥┦┧┨┩┪┫┃┇┋┴┵┶┷┸┹┺┻╋╊╉╈╇╆╅╄";
    const ignoreObj = {};
    for (let i = 0, j = ignoreChars.length; i < j; i++) {
      ignoreObj[ignoreChars.charCodeAt(i)] = true;
    }
    
    // 构建有限状态机
    // 生成的数据结构形制如下
    /**
     * {
     *   '王': {
     *     '八': {
     *       '蛋': {
     *         empty: true
     *       }
     *       '羔': {
     *         '子': {
     *           empty: true
     *         }
     *       }
     *     }
     *   }
     * }
     */
    function buildMap(wordList) {
      const result = {};
    
      for (let i = 0, len = wordList.length; i < len; ++i) {
        let map = result;
        const word = wordList[i];
        for (let j = 0; j < word.length; ++j) {
          const ch = word.charAt(j).toLowerCase();
          if (map[ch]) {
            map = map[ch];
            if (map.empty) {
              break;
            }
          } else {
            if (map.empty) {
              delete map.empty;
            }
            map[ch] = {
              empty: true,
            };
            map = map[ch];
          }
        }
      }
      return result;
    }
    
    // 获取敏感词列表
    function getSensitiveWords() {
      /*
      let words = [];
      if (words.length === 0) {
        const data = fs.readFileSync(path.join(__dirname, './words'), 'utf8');
        words = data.split('
    ');
      }
      return words.filter(item => !!item);
      */
      return [
        '王八蛋',
        '王八羔子'
      ]
    }
    
    // 获取敏感词库对象
    const sensitiveWords = getSensitiveWords();
    const map = buildMap(sensitiveWords);
    
    // 具体检测代码。
    function check(map, content) {
      const result = [];
      let stack = [];
      let point = map;
      for (let i = 0, len = content.length; i < len; ++i) {
        const code = content.charCodeAt(i);
        if (ignoreObj[code]) {
          continue;
        }
        const ch = content.charAt(i);
        const item = point[ch.toLowerCase()];
        if (!item) {
          i = i - stack.length;
          stack = [];
          point = map;
        } else if (item.empty) {
          stack.push(ch);
          result.push(stack.join(''));
          stack = [];
          point = map;
        } else {
          stack.push(ch);
          point = item;
        }
      }
      return result;
    }
    
    function checkSensitive(content) {
      const words = check(map, content);
      return words;
    }
    
    module.exports = checkSensitive;
    

    测试

    checkSensitive('老板黄鹤*王&八&(&蛋,吃喝嫖赌,欠下了3.5个亿,带着他的小姨子跑了')
    
    // ['王八蛋']
    

    缺陷

    若用火星文、异体字、同音字来替代,这类算法没什么好的办法能识别

    checkSensitive('王八羔仔')
    
    // []
    

    若敏感词短,则误伤很大,若前文设置的是 王八 作为敏感词。

    checkSensitive('虎躯一震,散发一阵王八之气');
    
    // ['王八']
    

    自然还有大名鼎鼎的 江阴毛纺织厂 怕也会很容易被误伤。所以,有更智能的办法是先做分词,参照分词库, 比如说 Node 库的 nodejieba,Python 的 jieba

    可以将 江阴毛纺织厂,拆解为 江阴毛纺织厂 的词后,在进行 DFA 模式匹配

    但截止日前笔者还不敢直接在线上库当中直接使用分词库,市面上大多数的敏感词过滤也不会上分词库的,或许是担心性能问题,也存在宁杀错无放过的心态吧。如果有人有更好的办法,请不吝赐教啊。

  • 相关阅读:
    C# 实现从程序集中抽取内嵌资源文件
    git clone 显著提速,解决Github代码拉取速度缓慢问题
    C# 将List数据 导出到csv 文件
    Nginx的location匹配规则-根据url 路径重定向到不同的IP
    WinForm最小化到托盘以及托盘右键菜单
    安卓手机浏览器调试-Debug模式
    C#-Forms.Timer、Timers.Timer、Threading.Timer的比较和使用
    批处理实现批量创建快捷方式
    设计模式(六)装饰模式(转)
    设计模式(五)桥接模式(转)
  • 原文地址:https://www.cnblogs.com/everlose/p/12855853.html
Copyright © 2020-2023  润新知