content: package org.superboot.utils;
  
  
  import java.io.BufferedReader;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.InputStreamReader;
  import java.util.*;
  
  /**
   * <b> 敏感词过滤 </b>
   * <p>
   * 功能描述:根据敏感词库过滤消息中的敏感词信息 敏感词库文件必须为：Sensitiveword.txt
   * </p>
   */
  public class SensitiveWordUtil {
  
      private String ENCODING = "UTF-8";    //字符编码
      //敏感词汇列表
      public Map sensitiveWordMap;
  
      public static int minMatchTYpe = 1;      //最小匹配规则
      public static int maxMatchType = 2;      //最大匹配规则
  
  
      /**
       * 通配符匹配
       *
       * @param pattern 通配符模式
       * @param str     待匹配的字符串 <a href="http://my.oschina.net/u/556800" target="_blank" rel="nofollow">@return</a>
       *                匹配成功则返回true，否则返回false
       */
      private static boolean wildcardMatch(String pattern, String str) {
          int patternLength = pattern.length();
          int strLength = str.length();
          int strIndex = 0;
          char ch;
          for (int patternIndex = 0; patternIndex < patternLength; patternIndex++) {
              ch = pattern.charAt(patternIndex);
              if (ch == '*') {
                  // 通配符星号*表示可以匹配任意多个字符
                  while (strIndex < strLength) {
                      if (wildcardMatch(pattern.substring(patternIndex + 1), str.substring(strIndex))) {
                          return true;
                      }
                      strIndex++;
                  }
              } else if (ch == '?') {
                  // 通配符问号?表示匹配任意一个字符
                  strIndex++;
                  if (strIndex > strLength) {
                      // 表示str中已经没有字符匹配?了。
                      return false;
                  }
              } else {
                  if ((strIndex >= strLength) || (ch != str.charAt(strIndex))) {
                      return false;
                  }
                  strIndex++;
              }
          }
          return strIndex == strLength;
      }
  
  
      /**
       * 递归查找文件
       *
       * @param baseDirName    查找的文件夹路径
       * @param targetFileName 需要查找的文件名
       */
      private static List<String> findFiles(String baseDirName, String targetFileName) {
          /**
           * 算法简述： 从某个给定的需查找的文件夹出发，搜索该文件夹的所有子文件夹及文件， 若为文件，则进行匹配，匹配成功则加入结果集，若为子文件夹，则进队列。 队列不空，重复上述操作，队列为空，程序结束，返回结果。
           */
          List<String> classFiles = new ArrayList<String>();
          String tempName = null;
          // 判断目录是否存在
          File baseDir = new File(baseDirName);
          if (!baseDir.exists() || !baseDir.isDirectory()) {
          } else {
              String[] filelist = baseDir.list();
              for (int i = 0; i < filelist.length; i++) {
                  File readfile = new File(baseDirName + File.separator + filelist[i]);
                  if (readfile.isDirectory()) {
                      classFiles.addAll(findFiles(baseDirName + File.separator + filelist[i], targetFileName));
                  } else {
                      tempName = readfile.getName();
                      if (targetFileName.toLowerCase().equals(tempName.toLowerCase())) {
                          String classname;
                          String tem = readfile.getAbsoluteFile().toString().replaceAll("\\\\", "/");
                          classFiles.add(tem);
                      }
                  }
              }
          }
          return classFiles;
      }
  
      /**
       * 读取敏感词库中的内容，将内容添加到set集合中
       *
       * @return
       * @throws Exception
       * @author chenming
       * @date 2014年4月20日 下午2:31:18
       * @version 1.0
       */
      @SuppressWarnings("resource")
      private Set<String> readSensitiveWordFile() throws Exception {
          Set<String> set = new HashSet<String>();
  
          //扫描项目中所有的敏感词库文件，统一加载到敏感词库中
          List<String> files = findFiles(System.getProperty("user.dir"), "Sensitiveword.txt");
          if (0 < files.size()) {
              for (String fileStr : files) {
                  File file = new File(fileStr);    //读取文件
                  InputStreamReader read = new InputStreamReader(new FileInputStream(file), ENCODING);
                  try {
                      if (file.isFile() && file.exists()) {      //文件流是否存在
                          BufferedReader bufferedReader = new BufferedReader(read);
                          String txt = null;
                          while ((txt = bufferedReader.readLine()) != null) {    //读取文件，将文件内容放入到set中
                              set.add(txt);
                          }
                      } else {
                          throw new Exception("敏感词库文件不存在");
                      }
                  } catch (Exception e) {
                      throw e;
                  } finally {
                      read.close();     //关闭文件流
                  }
              }
          }
  
          return set;
      }
  
  
      /**
       * 读取敏感词库，将敏感词放入HashSet中，构建一个DFA算法模型：<br>
       * 中 = {
       * isEnd = 0
       * 国 = {<br>
       * isEnd = 1
       * 人 = {isEnd = 0
       * 民 = {isEnd = 1}
       * }
       * 男  = {
       * isEnd = 0
       * 人 = {
       * isEnd = 1
       * }
       * }
       * }
       * }
       * 五 = {
       * isEnd = 0
       * 星 = {
       * isEnd = 0
       * 红 = {
       * isEnd = 0
       * 旗 = {
       * isEnd = 1
       * }
       * }
       * }
       * }
       *
       * @param keyWordSet 敏感词库
       * @author chenming
       * @date 2014年4月20日 下午3:04:20
       * @version 1.0
       */
      @SuppressWarnings({"rawtypes", "unchecked"})
      private void addSensitiveWordToHashMap(Set<String> keyWordSet) {
          sensitiveWordMap = new HashMap(keyWordSet.size());     //初始化敏感词容器，减少扩容操作
          String key = null;
          Map nowMap = null;
          Map<String, String> newWorMap = null;
          //迭代keyWordSet
          Iterator<String> iterator = keyWordSet.iterator();
          while (iterator.hasNext()) {
              key = iterator.next();    //关键字
              nowMap = sensitiveWordMap;
              for (int i = 0; i < key.length(); i++) {
                  char keyChar = key.charAt(i);       //转换成char型
                  Object wordMap = nowMap.get(keyChar);       //获取
  
                  if (wordMap != null) {        //如果存在该key，直接赋值
                      nowMap = (Map) wordMap;
                  } else {     //不存在则，则构建一个map，同时将isEnd设置为0，因为他不是最后一个
                      newWorMap = new HashMap<String, String>();
                      newWorMap.put("isEnd", "0");     //不是最后一个
                      nowMap.put(keyChar, newWorMap);
                      nowMap = newWorMap;
                  }
  
                  if (i == key.length() - 1) {
                      nowMap.put("isEnd", "1");    //最后一个
                  }
              }
          }
      }
  
      /**
       * 初始化敏感词汇
       *
       * @return
       */
      public Map initKeyWord() {
          try {
              //读取敏感词库
              Set<String> keyWordSet = readSensitiveWordFile();
              //将敏感词库加入到HashMap中
              addSensitiveWordToHashMap(keyWordSet);
          } catch (Exception e) {
              e.printStackTrace();
          }
          return sensitiveWordMap;
      }
  
  
      /**
       * 构造函数，初始化敏感词库
       */
      public SensitiveWordUtil() {
          sensitiveWordMap = initKeyWord();
      }
  
  
      /**
       * 判断文字是否包含敏感字符
       *
       * @param txt       文字
       * @param matchType 匹配规则&nbsp;1：最小匹配规则，2：最大匹配规则
       * @return 若包含返回true，否则返回false
       * @author chenming
       * @date 2014年4月20日 下午4:28:30
       * @version 1.0
       */
      public boolean isContaintSensitiveWord(String txt, int matchType) {
          boolean flag = false;
          for (int i = 0; i < txt.length(); i++) {
              int matchFlag = this.CheckSensitiveWord(txt, i, matchType); //判断是否包含敏感字符
              if (matchFlag > 0) {    //大于0存在，返回true
                  flag = true;
              }
          }
          return flag;
      }
  
      /**
       * 获取文字中的敏感词
       *
       * @param txt       文字
       * @param matchType 匹配规则&nbsp;1：最小匹配规则，2：最大匹配规则
       * @return
       * @author chenming
       * @date 2014年4月20日 下午5:10:52
       * @version 1.0
       */
      public Set<String> getSensitiveWord(String txt, int matchType) {
          Set<String> sensitiveWordList = new HashSet<String>();
  
          for (int i = 0; i < txt.length(); i++) {
              int length = CheckSensitiveWord(txt, i, matchType);    //判断是否包含敏感字符
              if (length > 0) {    //存在,加入list中
                  sensitiveWordList.add(txt.substring(i, i + length));
                  i = i + length - 1;    //减1的原因，是因为for会自增
              }
          }
  
          return sensitiveWordList;
      }
  
      /**
       * 替换敏感字字符
       *
       * @param txt
       * @param matchType
       * @param replaceChar 替换字符，默认*
       * @author chenming
       * @date 2014年4月20日 下午5:12:07
       * @version 1.0
       */
      public String replaceSensitiveWord(String txt, int matchType, String replaceChar) {
          String resultTxt = txt;
          Set<String> set = getSensitiveWord(txt, matchType);     //获取所有的敏感词
          Iterator<String> iterator = set.iterator();
          String word = null;
          String replaceString = null;
          while (iterator.hasNext()) {
              word = iterator.next();
              replaceString = getReplaceChars(replaceChar, word.length());
              resultTxt = resultTxt.replaceAll(word, replaceString);
          }
  
          return resultTxt;
      }
  
      /**
       * 获取替换字符串
       *
       * @param replaceChar
       * @param length
       * @return
       * @author chenming
       * @date 2014年4月20日 下午5:21:19
       * @version 1.0
       */
      private String getReplaceChars(String replaceChar, int length) {
          String resultReplace = replaceChar;
          for (int i = 1; i < length; i++) {
              resultReplace += replaceChar;
          }
  
          return resultReplace;
      }
  
      /**
       * 检查文字中是否包含敏感字符，检查规则如下：<br>
       *
       * @param txt
       * @param beginIndex
       * @param matchType
       * @author chenming
       * @date 2014年4月20日 下午4:31:03
       * @return，如果存在，则返回敏感词字符的长度，不存在返回0
       * @version 1.0
       */
      @SuppressWarnings({"rawtypes"})
      public int CheckSensitiveWord(String txt, int beginIndex, int matchType) {
          boolean flag = false;    //敏感词结束标识位：用于敏感词只有1位的情况
          int matchFlag = 0;     //匹配标识数默认为0
          char word = 0;
          Map nowMap = sensitiveWordMap;
          for (int i = beginIndex; i < txt.length(); i++) {
              word = txt.charAt(i);
              nowMap = (Map) nowMap.get(word);     //获取指定key
              if (nowMap != null) {     //存在，则判断是否为最后一个
                  matchFlag++;     //找到相应key，匹配标识+1
                  if ("1".equals(nowMap.get("isEnd"))) {       //如果为最后一个匹配规则,结束循环，返回匹配标识数
                      flag = true;       //结束标志位为true
                      if (SensitiveWordUtil.minMatchTYpe == matchType) {    //最小规则，直接返回,最大规则还需继续查找
                          break;
                      }
                  }
              } else {     //不存在，直接返回
                  break;
              }
          }
          if (matchFlag < 2 || !flag) {        //长度必须大于等于1，为词
              matchFlag = 0;
          }
          return matchFlag;
      }
  
  
      public static void main(String[] args) {
          SensitiveWordUtil filter = new SensitiveWordUtil();
          System.out.println("敏感词的数量：" + filter.sensitiveWordMap.size());
          String string = "太多的伤感情怀也许只局限于饲养基地狗日的 荧幕中操你吗的情节，主人公尝试着去用某种方式渐渐的很潇洒地释自杀指南怀那些自己经历的伤感。"
                  + "然后法轮功 我们的扮演的角色就是跟随着主人公的喜红客联盟 怒哀乐而过于牵强的把自己的情感也附加于银幕情节中，然后感动就流泪，"
                  + "难过就躺在某一个人的怀里尽情的阐述心西藏扉或者手机卡复制器一个人一杯红酒一部电影视频来源在夜三 级 片 深人静的晚上qizin.net，张春桥关上电话静静的发呆着。胡的接班人";
          System.out.println("待检测语句字数：" + string.length());
          long beginTime = System.currentTimeMillis();
          Set<String> set = filter.getSensitiveWord(string, 1);
          long endTime = System.currentTimeMillis();
          System.out.println("语句中包含敏感词的个数为：" + set.size() + "。包含：" + set);
          System.out.println("总共消耗时间为：" + (endTime - beginTime));
      }
  }
  