本文转自浅析敏感词过滤算法(C++),自己也在其基础上根据自己的情况做了一点修改。

https://blog.csdn.net/u012755940/article/details/51689401?utm_source=app

为了提高查找效率,这里将敏感词用树形结构存储,每个节点有一个map成员,其映射关系为一个string对应一个WordNode。 
比如敏感词库里面有枪手、手枪这几个词,读入后就变成了如下图所示的树状结构。 

STL::map是按照operator<比较判断元素是否相同,以及比较元素的大小,然后选择合适的位置插入到树中。 
下面主要实现了WordNode类,进行节点的插入以及查询。

WordNode.h

  1. #ifndef __WORDNODE_H__
  2. #define __WORDNODE_H__
  3.  
  4. #define PACE 1
  5.  
  6. #include <string>
  7. #include <map>
  8. #include <stdio.h>
  9.  
  10. class CWordNode
  11. {
  12. public:
  13. CWordNode(std::string character);
  14. CWordNode(){ m_character = ""; };
  15. ~CWordNode();
  16. std::string getCharacter() const{ return m_character; };
  17. CWordNode* findChild(std::string& nextCharacter);
  18. CWordNode* insertChild(std::string& nextCharacter);
  19. private:
  20. friend class CWordTree;
  21. typedef std::map<std::string, CWordNode> _TreeMap;
  22. typedef std::map<std::string, CWordNode>::iterator _TreeMapIterator;
  23.  
  24. std::string m_character;
  25. _TreeMap m_map;
  26. CWordNode* m_parent;
  27. };
  28.  
  29. #endif

WordNode.cpp

  1. #include "WordNode.h"
  2.  
  3. using namespace std;
  4.  
  5. CWordNode::~CWordNode()
  6. {
  7.  
  8. }
  9.  
  10. CWordNode::CWordNode(std::string character)
  11. {
  12. if (character.size() == PACE)
  13. {
  14. m_character.assign(character);
  15. }
  16. }
  17.  
  18. CWordNode* CWordNode::findChild(std::string& nextCharacter)
  19. {
  20. _TreeMapIterator TreeMapIt = m_map.find(nextCharacter);
  21. if (TreeMapIt == m_map.end())
  22. {
  23. return NULL;
  24. }
  25. else
  26. {
  27. return &TreeMapIt->second;
  28. }
  29. }
  30.  
  31. CWordNode* CWordNode::insertChild(std::string& nextCharacter)
  32. {
  33. if (!findChild(nextCharacter))
  34. {
  35. m_map.insert(pair<std::string, CWordNode>(nextCharacter, CWordNode(nextCharacter)));
  36. return &(m_map.find(nextCharacter)->second);
  37. }
  38. return NULL;
  39. }

另外,

  1. #define PACE 1

这里的PACE原本是2,因为一个GBK汉字占两个字符,而且原文中也说了如果需要考虑英文或中英文结合的情况,将PACE改为1。 
不过我试过之后,觉得不管是中文、英文还是中英文,PACE为 1 都适用,结果都没错,只不过中文的情况下每个节点的string都不再是一个完整的汉字,而是汉字的一个字符。

接下来实现这个tree,在建立WordNode树时,以parent为根节点建立,一开始parent为m_emptyRoot,然后keyword按照规则添加到树中,假设一开始m_emptyRoot为空,keyword为”敏感词”,则会以”敏感词”为一条分支建立成为一颗树枝’敏’->’感’->’词’,此后,若想再添加”敏感度”,由于”敏感词”与”敏感度”的前两个字相同,则会在’敏’->’感’->’词’的基础上,从字’感’开始新生长出一颗分支,即’敏’->’感’->’度’,这两颗分支共用’敏’->’感’。

下面代码实现了WordTree类,进行树的构成及查询。

WordTree.h

  1. #ifndef __WORDTREE_H__
  2. #define __WORDTREE_H__
  3.  
  4. #include "WordNode.h"
  5.  
  6. class CWordTree
  7. {
  8. public:
  9. CWordTree();
  10. ~CWordTree();
  11.  
  12. int nCount;
  13. CWordNode* insert(std::string &keyWord);
  14. CWordNode* insert(const char* keyword);
  15. CWordNode* find(std::string& keyword);
  16. private:
  17. CWordNode m_emptyRoot;
  18. int m_pace;
  19. CWordNode* insert(CWordNode* parent, std::string& keyword);
  20. CWordNode* insertBranch(CWordNode* parent, std::string& keyword);
  21. CWordNode* find(CWordNode* parent, std::string& keyword);
  22. };
  23.  
  24. #endif // __WORDTREE_H__

WordTree.cpp

  1. #include "WordTree.h"
  2.  
  3. CWordTree::CWordTree()
  4. :nCount(0)
  5. {
  6.  
  7. }
  8.  
  9. CWordTree::~CWordTree()
  10. {
  11. }
  12.  
  13. CWordNode* CWordTree::insert(std::string &keyWord)
  14. {
  15. return insert(&m_emptyRoot, keyWord);
  16. }
  17.  
  18. CWordNode* CWordTree::insert(const char* keyWord)
  19. {
  20. std::string wordstr(keyWord);
  21. return insert(wordstr);
  22. }
  23.  
  24. CWordNode* CWordTree::insert(CWordNode* parent, std::string& keyWord)
  25. {
  26. if (keyWord.size() == 0)
  27. {
  28. return NULL;
  29. }
  30. std::string firstChar = keyWord.substr(0, PACE);
  31. CWordNode* firstNode = parent->findChild(firstChar);
  32. if (firstNode == NULL)
  33. {
  34. return insertBranch(parent, keyWord);
  35. }
  36. std::string restChar = keyWord.substr(PACE, keyWord.size());
  37. return insert(firstNode, restChar);
  38. }
  39.  
  40. CWordNode* CWordTree::find(std::string& keyWord)
  41. {
  42. return find(&m_emptyRoot, keyWord);
  43. }
  44.  
  45. CWordNode* CWordTree::find(CWordNode* parent, std::string& keyWord)
  46. {
  47. std::string firstChar = keyWord.substr(0, PACE);
  48. CWordNode* firstNode = parent->findChild(firstChar);
  49. if (firstNode == NULL)
  50. {
  51. nCount = 0;
  52. return NULL;
  53. }
  54. std::string restChar = keyWord.substr(PACE, keyWord.size());
  55. if (firstNode->m_map.empty())
  56. {
  57. return firstNode;
  58. }
  59. if (keyWord.size() == PACE)
  60. {
  61. return NULL;
  62. }
  63. nCount++;
  64. return find(firstNode, restChar);
  65. }
  66.  
  67. CWordNode* CWordTree::insertBranch(CWordNode* parent, std::string& keyWord)
  68. {
  69. std::string firstChar = keyWord.substr(0, PACE);
  70. CWordNode* firstNode = parent->insertChild(firstChar);
  71. if (firstNode != NULL)
  72. {
  73. std::string restChar = keyWord.substr(PACE, keyWord.size());
  74. if (!restChar.empty())
  75. {
  76. return insertBranch(firstNode, restChar);
  77. }
  78. }
  79. return NULL;
  80. }

最后就是利用上述的Tree来实现敏感词过滤,WordFilter::censor(string &source) 函数用来进行敏感词过滤,source即输入的字符串,如果source包含敏感词,则用“**”替代掉。

WordFilter::load(const char* filepath) 函数通过文件载入敏感词,并构建WordTree,这里我用的是txt文件。

下面实现了WordFilter类。

WordFilter.h

  1. #ifndef __WORDFILTER_H__
  2. #define __WORDFILTER_H__
  3.  
  4. #include "WordTree.h"
  5. #include "base/CCRef.h"
  6.  
  7. USING_NS_CC;
  8.  
  9. class CWordFilter : public Ref
  10. {
  11. public:
  12. ~CWordFilter();
  13. bool loadFile(const char* filepath);
  14. bool censorStr(std::string &source);
  15. bool censorStrWithOutSymbol(const std::string &source);
  16. static CWordFilter* getInstance();
  17. static void release();
  18. private:
  19. std::string string_To_UTF8(const std::string & str);
  20. std::string UTF8_To_string(const std::string & str);
  21. CWordFilter();
  22. static CWordFilter* m_pInstance;
  23. CWordTree m_WordTree;
  24. };
  25.  
  26. #endif // __WORDFILTER_H__

WordFilter.cpp

  1. #include "WordFilter.h"
  2. #include <ctype.h>
  3. #include <algorithm>
  4. #include <iostream>
  5. #include <fstream>
  6. #include <istream>
  7.  
  8. using namespace std;
  9.  
  10. USING_NS_CC;
  11.  
  12. CWordFilter* CWordFilter::m_pInstance = nullptr;
  13. CWordFilter::CWordFilter()
  14. {
  15. }
  16.  
  17. CWordFilter::~CWordFilter()
  18. {
  19. }
  20.  
  21. CWordFilter* CWordFilter::getInstance()
  22. {
  23. if (m_pInstance == NULL)
  24. {
  25. m_pInstance = new CWordFilter();
  26. }
  27. return m_pInstance;
  28. }
  29.  
  30. void CWordFilter::release()
  31. {
  32. if (m_pInstance)
  33. {
  34. delete m_pInstance;
  35. }
  36. m_pInstance = NULL;
  37. }
  38.  
  39. bool CWordFilter::loadFile(const char* filepath)
  40. {
  41. ifstream infile(filepath, ios::in);
  42.  
  43. if (!infile)
  44. {
  45. return false;
  46. }
  47. else
  48. {
  49. string read;
  50. while (getline(infile, read))
  51. {
  52. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
  53. string s;
  54. s = read.substr(0, read.length() - 1);
  55. m_WordTree.insert(s);
  56. #else
  57. m_WordTree.insert(read);
  58. #endif
  59. }
  60. }
  61.  
  62. infile.close();
  63. return true;
  64. }
  65.  
  66. bool CWordFilter::censorStr(string &source)
  67. {
  68. int lenght = source.size();
  69. for (int i = 0; i < lenght; i += 1)
  70. {
  71. string substring = source.substr(i, lenght - i);
  72. if (m_WordTree.find(substring) != NULL)
  73. {
  74. source.replace(i, (m_WordTree.nCount + 1), "**");
  75. lenght = source.size();
  76. return true;
  77. }
  78. }
  79. return false;
  80. }
  81.  
  82. bool CWordFilter::censorStrWithOutSymbol(const std::string &source)
  83. {
  84. string sourceWithOutSymbol;
  85.  
  86. int i = 0;
  87. while (source[i] != 0)
  88. {
  89. if (source[i] & 0x80 && source[i] & 0x40 && source[i] & 0x20)
  90. {
  91. int byteCount = 0;
  92. if (source[i] & 0x10)
  93. {
  94. byteCount = 4;
  95. }
  96. else
  97. {
  98. byteCount = 3;
  99. }
  100. for (int a = 0; a < byteCount; a++)
  101. {
  102. sourceWithOutSymbol += source[i];
  103. i++;
  104. }
  105. }
  106. else if (source[i] & 0x80 && source[i] & 0x40)
  107. {
  108. i += 2;
  109. }
  110. else
  111. {
  112. i += 1;
  113. }
  114. }
  115. return censorStr(sourceWithOutSymbol);
  116. }

这里说明一点,本人是做Cocos2d-x手游客户端开发的,程序是要移植到安卓或者iOS平台上。当逐行读取txt文件中的敏感词并构成树的时候,getline(infile, read)函数得到的read字符串后面带有结束符,比如“枪手\0”,这时跟我们需要检测的字符串“…枪手…”就明显不符合,这是检测不出来的。这种情况我现在只知道在安卓或者iOS平台存在,而在windows环境下VS中是不会出现这种问题的。所以我对读取到的字符串做了处理,把最后一个字符也就是结束符去掉,再进行下一步操作。

而我使用的是lua,lua发送给C++的字符串都是用utf-8编码的,所以再去除字符串的时候并不能简答的使用(a & 0x80)来判断

基于DFA算法实现的敏感词过滤的更多相关文章

  1. DFA算法之内容敏感词过滤

    DFA 算法是通过提前构造出一个 树状查找结构,之后根据输入在该树状结构中就可以进行非常高效的查找. 设我们有一个敏感词库,词酷中的词汇为:我爱你我爱他我爱她我爱你呀我爱他呀我爱她呀我爱她啊 那么就可 ...

  2. Java实现敏感词过滤 - IKAnalyzer中文分词工具

    IKAnalyzer 是一个开源的,基于java语言开发的轻量级的中文分词工具包. 官网: https://code.google.com/archive/p/ik-analyzer/ 本用例借助 I ...

  3. 基于DFA算法、RegExp对象和vee-validate实现前端敏感词过滤

    面临敏感词过滤的问题,最简单的方案就是对要检测的文本,遍历所有敏感词,逐个检测输入的文本是否包含指定的敏感词. 很明显上面这种实现方法的检测时间会随着敏感词库数量的增加而线性增加.系统会因此面临性能和 ...

  4. web系统安全运营之基础- 基于DFA算法的高性能的敏感词,脏词的检测过滤算法类(c#).

    [概述]做好一个web系统的安全运维,除了常规的防注入,防入侵等,还有一个检测并过滤敏感词,脏词..  这件事做得不好,轻则导致一场投诉或纠纷,重则导致产品被勒令关闭停运. 废话少说,先看下代码,可以 ...

  5. java实现敏感词过滤(DFA算法)

    小Alan在最近的开发中遇到了敏感词过滤,便去网上查阅了很多敏感词过滤的资料,在这里也和大家分享一下自己的理解. 敏感词过滤应该是不用给大家过多的解释吧?讲白了就是你在项目中输入某些字(比如输入xxo ...

  6. Java实现敏感词过滤 - DFA算法

    Java实现DFA算法进行敏感词过滤 封装工具类如下: 使用前需对敏感词库进行初始化: SensitiveWordUtil.init(sensitiveWordSet); package cn.swf ...

  7. 敏感词过滤的算法原理之DFA算法

    参考文档 http://blog.csdn.net/chenssy/article/details/26961957 敏感词.文字过滤是一个网站必不可少的功能,如何设计一个好的.高效的过滤算法是非常有 ...

  8. DFA和trie特里实现敏感词过滤(python和c语言)

    今天的项目是与完成python开展,需要使用做关键词检查,筛选分类,使用前c语言做这种事情.有了线索,非常高效,内存小了,检查快. 到达python在,第一个想法是pip基于外观的c语言python特 ...

  9. 超强敏感词过滤算法第二版 可以忽略大小写、全半角、简繁体、特殊符号、HTML标签干扰

    上一篇 发一个高性能的敏感词过滤算法 可以忽略大小写.全半角.简繁体.特殊符号干扰 改进主要有几点: 用BitArray取代Dictionary用空间换时间 性能进一步提升 大概会增加词库的  6k* ...

  10. 5分钟构建无服务器敏感词过滤后端系统(基于FunctionGraph)

    摘要:开发者通过函数工作流,无需配置和管理服务器,以无服务器的方式构建应用,便能开发出一个弹性高可用的后端系统.托管函数具备以毫秒级弹性伸缩.免运维.高可靠的方式运行,极大地提高了开发和运维效率,减小 ...

随机推荐

  1. 【笔记向】RESTful api

    RESTful api 是什么 源自论文:点我,中文版 Representational state transfer: Representational:数据表现形式 state:状态 transf ...

  2. 使用Addressables.LoadAssetAsync<Asset>(target)加载unity资源,不止是gameobject

    要声明的方法: public static async Task<string> ReadJsonData(string target){  TextAsset jsonDataObjec ...

  3. docker02-centos上安装

    1.前提说明Docker 运行在 CentOS 7 上,要求系统为64位.系统内核版本为 3.10 以上.Docker 运行在 CentOS-6.5 或更高的版本的 CentOS 上,要求系统为64位 ...

  4. 四:SSM框架整合

    四:SSM框架整合 Spring+Spring Mvc+Mybatis整合: 代码实现: 1.创建maven工程 pom.xml   <dependencies><!--  spri ...

  5. RxJava 异常时堆栈显示不正确?解决方法都在这里

    本文首发我的博客,github 地址 大家好,我是徐公,今天为大家带来的是 RxJava 的一个血案,一行代码 return null 引发的. 前阵子,组内的同事反馈说 RxJava 在 debug ...

  6. Vue学习:实现用户没有登陆时,访问后自动跳转登录页面

    设计思路 定义路由的时候配置属性,这里使用needLogin标记访问页面是否需要登录 设置路由守卫,每个页面在跳转之前都要经过验证,校验用户信息是否存在,不存在跳转到登录页 用户登录后将用户信息存储在 ...

  7. 基于SpringBoot实现操作GaussDB(DWS)的项目实战

    摘要:本文就使用springboot结合mybatis plus在项目中实现对GaussDB(DWS)的增删改查操作. 本文分享自华为云社区<基于SpringBoot实现操作GaussDB(DW ...

  8. 【雅礼联考DAY01】数列

    #include<cstdio> #include<map> using namespace std; typedef long long LL; const int N = ...

  9. HDMI转USB视频采集卡(ACASIS 1080P高清视频采集卡)--九五小庞

    ACASIS阿卡西斯是深圳市菲德越科技有限公司旗下数码科技品牌.菲德越是2008年成立的一家专注于采集卡.硬盘盒.集线器等专业3C配件产品,集研发.设计.生产.销售于一体的高新科技公司,我们公司以向客 ...

  10. MySQL创建表的三种方式

    创建表的三种方式 通过create语句直接创建 语法: create [TEMPORARY] table [IF NOT EXISTS] table_name ( col_name column_de ...