正在显示
5 个修改的文件
包含
82 行增加
和
514 行删除
| 1 | -# 高效敏感词过滤 | 1 | +# 快速敏感词过滤 |
| 2 | 2 | ||
| 3 | 3 | ||
| 4 | -## 性能概述 | 4 | +### 性能概述 |
| 5 | 5 | ||
| 6 | -在共60M穿越小说上测试,单核性能为80M字符每秒(i7 2.3GHz)。 | ||
| 7 | -相比类似原理的正向最大匹配分词,性能一般在1M字节每秒左右有很大提升,类似的优化方式可以用在分词器上。 | 6 | +使用60MB大小的小说测试,单核性能超过50M字符每秒(i7 2.3GHz)。 |
| 8 | 7 | ||
| 9 | ``` | 8 | ``` |
| 10 | -敏感词 14580 条 | ||
| 11 | -共加载 599254 行,30613005 字符。 | ||
| 12 | -共耗时 0.381 秒, 速度为 80349.1字符/毫秒 | 9 | +敏感词 14553 条 |
| 10 | +待过滤文本共 599254 行,30613005 字符。 | ||
| 11 | +过滤耗时 0.535 秒, 速度为 57220.6字符/毫秒 | ||
| 12 | +其中 39691 行有替换 | ||
| 13 | ``` | 13 | ``` |
| 14 | 14 | ||
| 15 | -## 优化方式 | 15 | +### 优化方式 |
| 16 | 16 | ||
| 17 | 主要的优化目标是速度,从以下方面优化: | 17 | 主要的优化目标是速度,从以下方面优化: |
| 18 | 18 | ||
| @@ -22,471 +22,37 @@ | @@ -22,471 +22,37 @@ | ||
| 22 | 4. StringPointer,在不生成新实例的情况下计算任意位置2个字符的hash和mix | 22 | 4. StringPointer,在不生成新实例的情况下计算任意位置2个字符的hash和mix |
| 23 | 5. StringPointer,尽量减少实例生成和char数组的拷贝。 | 23 | 5. StringPointer,尽量减少实例生成和char数组的拷贝。 |
| 24 | 24 | ||
| 25 | -## 敏感词库 | ||
| 26 | - | ||
| 27 | -自带敏感词库拷贝自 https://github.com/observerss/textfilter ,并删除如`女人`、`然后`这样的几个常用词。 | ||
| 28 | -如果需要自带敏感词的实例,可以直接使用下面的方式: | 25 | +### 敏感词库 |
| 29 | 26 | ||
| 27 | +默认敏感词库拷贝自 https://github.com/observerss/textfilter ,并删除如`女人`、`然后`这样的几个常用词。 | ||
| 28 | +使用默认敏感词库的示例如下 | ||
| 30 | 29 | ||
| 31 | ```java | 30 | ```java |
| 32 | -// 使用默认的单例(即加载了自带敏感词库的) | 31 | +// 使用默认单例(加载默认敏感词库) |
| 33 | SensitiveFilter filter = SensitiveFilter.DEFAULT; | 32 | SensitiveFilter filter = SensitiveFilter.DEFAULT; |
| 34 | -// 对一个句子过滤 | ||
| 35 | -System.out.println(filter.filter("会上,主席进行了发言。", '*')); | 33 | +// 向过滤器增加一个词 |
| 34 | +filter.put("婚礼上唱春天在哪里"); | ||
| 35 | + | ||
| 36 | +// 待过滤的句子 | ||
| 37 | +String sentence = "然后,市长在婚礼上唱春天在哪里。"; | ||
| 38 | +// 进行过滤 | ||
| 39 | +String filted = filter.filter(sentence, '*'); | ||
| 40 | + | ||
| 41 | +// 如果未过滤,则返回输入的String引用 | ||
| 42 | +if(sentence != filted){ | ||
| 43 | + // 句子中有敏感词 | ||
| 44 | + System.out.println(filted); | ||
| 45 | +} | ||
| 36 | ``` | 46 | ``` |
| 37 | 47 | ||
| 38 | 打印结果 | 48 | 打印结果 |
| 39 | 49 | ||
| 40 | ``` | 50 | ``` |
| 41 | -会上,**进行了发言。 | ||
| 42 | -``` | ||
| 43 | - | ||
| 44 | -## 代码只有3个类直接贴上 | ||
| 45 | - | ||
| 46 | - | ||
| 47 | -### SensitiveFilter.java | ||
| 48 | -```java | ||
| 49 | -package com.odianyun.util.sensi; | ||
| 50 | - | ||
| 51 | -import java.io.BufferedReader; | ||
| 52 | -import java.io.IOException; | ||
| 53 | -import java.io.InputStreamReader; | ||
| 54 | -import java.io.Serializable; | ||
| 55 | -import java.nio.charset.StandardCharsets; | ||
| 56 | - | ||
| 57 | -/** | ||
| 58 | - * 敏感词过滤器,以过滤速度优化为主。<br/> | ||
| 59 | - * * 增加一个敏感词:{@link #put(String)} <br/> | ||
| 60 | - * * 过滤一个句子:{@link #filter(String, char)} <br/> | ||
| 61 | - * * 获取默认的单例:{@link #DEFAULT} | ||
| 62 | - * | ||
| 63 | - * @author ZhangXiaoye | ||
| 64 | - * @date 2017年1月5日 下午4:18:38 | ||
| 65 | - */ | ||
| 66 | -public class SensitiveFilter implements Serializable{ | ||
| 67 | - | ||
| 68 | - private static final long serialVersionUID = 1L; | ||
| 69 | - | ||
| 70 | - /** | ||
| 71 | - * 默认的单例,使用自带的敏感词库 | ||
| 72 | - */ | ||
| 73 | - public static final SensitiveFilter DEFAULT = new SensitiveFilter( | ||
| 74 | - new BufferedReader(new InputStreamReader( | ||
| 75 | - ClassLoader.getSystemResourceAsStream("sensi_words.txt") | ||
| 76 | - , StandardCharsets.UTF_8))); | ||
| 77 | - | ||
| 78 | - /** | ||
| 79 | - * 为2的n次方,考虑到敏感词大概在10k左右, | ||
| 80 | - * 这个数量应为词数的数倍,使得桶很稀疏 | ||
| 81 | - * 提高不命中时hash指向null的概率, | ||
| 82 | - * 加快访问速度。 | ||
| 83 | - */ | ||
| 84 | - static final int DEFAULT_INITIAL_CAPACITY = 131072; | ||
| 85 | - | ||
| 86 | - /** | ||
| 87 | - * 类似HashMap的桶,比较稀疏。 | ||
| 88 | - * 使用2个字符的hash定位。 | ||
| 89 | - */ | ||
| 90 | - protected SensitiveNode[] nodes = new SensitiveNode[DEFAULT_INITIAL_CAPACITY]; | ||
| 91 | - | ||
| 92 | - /** | ||
| 93 | - * 构建一个空的filter | ||
| 94 | - * | ||
| 95 | - * @author ZhangXiaoye | ||
| 96 | - * @date 2017年1月5日 下午4:18:07 | ||
| 97 | - */ | ||
| 98 | - public SensitiveFilter(){ | ||
| 99 | - | ||
| 100 | - } | ||
| 101 | - | ||
| 102 | - /** | ||
| 103 | - * 加载一个文件中的词典,并构建filter<br/> | ||
| 104 | - * 文件中,每行一个敏感词条<br/> | ||
| 105 | - * <b>注意:</b>读取完成后会调用{@link BufferedReader#close()}方法。<br/> | ||
| 106 | - * <b>注意:</b>读取中的{@link IOException}不会抛出 | ||
| 107 | - * | ||
| 108 | - * @param reader | ||
| 109 | - * @author ZhangXiaoye | ||
| 110 | - * @date 2017年1月5日 下午4:21:06 | ||
| 111 | - */ | ||
| 112 | - public SensitiveFilter(BufferedReader reader){ | ||
| 113 | - try{ | ||
| 114 | - for(String line = reader.readLine(); line != null; line = reader.readLine()){ | ||
| 115 | - put(line); | ||
| 116 | - } | ||
| 117 | - reader.close(); | ||
| 118 | - }catch(IOException e){ | ||
| 119 | - e.printStackTrace(); | ||
| 120 | - } | ||
| 121 | - } | ||
| 122 | - | ||
| 123 | - /** | ||
| 124 | - * 增加一个敏感词,如果词的长度(trim后)小于2,则丢弃<br/> | ||
| 125 | - * 此方法(构建)并不是主要的性能优化点。 | ||
| 126 | - * | ||
| 127 | - * @param word | ||
| 128 | - * @author ZhangXiaoye | ||
| 129 | - * @date 2017年1月5日 下午2:35:21 | ||
| 130 | - */ | ||
| 131 | - public void put(String word){ | ||
| 132 | - if(word == null || word.trim().length() < 2){ | ||
| 133 | - return; | ||
| 134 | - } | ||
| 135 | - StringPointer sp = new StringPointer(word.trim()); | ||
| 136 | - // 计算头两个字符的hash | ||
| 137 | - int hash = sp.nextTwoCharHash(0); | ||
| 138 | - // 计算头两个字符的mix表示(mix相同,两个字符相同) | ||
| 139 | - int mix = sp.nextTwoCharMix(0); | ||
| 140 | - // 转为在hash桶中的位置 | ||
| 141 | - int index = hash & (nodes.length - 1); | ||
| 142 | - | ||
| 143 | - // 从桶里拿第一个节点 | ||
| 144 | - SensitiveNode node = nodes[index]; | ||
| 145 | - if(node == null){ | ||
| 146 | - // 如果没有节点,则放进去一个 | ||
| 147 | - node = new SensitiveNode(mix); | ||
| 148 | - // 并添加词 | ||
| 149 | - node.words.add(sp); | ||
| 150 | - // 放入桶里 | ||
| 151 | - nodes[index] = node; | ||
| 152 | - }else{ | ||
| 153 | - // 如果已经有节点(1个或多个),找到正确的节点 | ||
| 154 | - for(;node != null; node = node.next){ | ||
| 155 | - // 匹配节点 | ||
| 156 | - if(node.headTwoCharMix == mix){ | ||
| 157 | - node.words.add(sp); | ||
| 158 | - return; | ||
| 159 | - } | ||
| 160 | - // 如果匹配到最后仍然不成功,则追加一个节点 | ||
| 161 | - if(node.next == null){ | ||
| 162 | - new SensitiveNode(mix, node).words.add(sp); | ||
| 163 | - return; | ||
| 164 | - } | ||
| 165 | - } | ||
| 166 | - } | ||
| 167 | - } | ||
| 168 | - | ||
| 169 | - /** | ||
| 170 | - * 对句子进行敏感词过滤 | ||
| 171 | - * | ||
| 172 | - * @param sentence 句子 | ||
| 173 | - * @param replace 敏感词的替换字符 | ||
| 174 | - * @return 过滤后的句子 | ||
| 175 | - * @author ZhangXiaoye | ||
| 176 | - * @date 2017年1月5日 下午4:16:31 | ||
| 177 | - */ | ||
| 178 | - public String filter(String sentence, char replace){ | ||
| 179 | - // 先转换为StringPointer | ||
| 180 | - StringPointer sp = new StringPointer(sentence); | ||
| 181 | - | ||
| 182 | - // 标示是否替换 | ||
| 183 | - boolean replaced = false; | ||
| 184 | - | ||
| 185 | - // 匹配的起始位置 | ||
| 186 | - int i = 0; | ||
| 187 | - while(i < sp.length - 2){ | ||
| 188 | - /* | ||
| 189 | - * 移动到下一个匹配位置的步进: | ||
| 190 | - * 如果未匹配为1,如果匹配是匹配的词长度 | ||
| 191 | - */ | ||
| 192 | - int step = 1; | ||
| 193 | - // 计算此位置开始2个字符的hash | ||
| 194 | - int hash = sp.nextTwoCharHash(i); | ||
| 195 | - /* | ||
| 196 | - * 根据hash获取第一个节点, | ||
| 197 | - * 真正匹配的节点可能不是第一个, | ||
| 198 | - * 所以有后面的for循环。 | ||
| 199 | - */ | ||
| 200 | - SensitiveNode node = nodes[hash & (nodes.length - 1)]; | ||
| 201 | - /* | ||
| 202 | - * 如果非敏感词,node基本为null。 | ||
| 203 | - * 这一步大幅提升效率 | ||
| 204 | - */ | ||
| 205 | - if(node != null){ | ||
| 206 | - /* | ||
| 207 | - * 如果能拿到第一个节点, | ||
| 208 | - * 才计算mix(mix相同表示2个字符相同)。 | ||
| 209 | - * mix的意义和HashMap先hash再equals的equals部分类似。 | ||
| 210 | - */ | ||
| 211 | - int mix = sp.nextTwoCharMix(i); | ||
| 212 | - /* | ||
| 213 | - * 循环所有的节点,如果非敏感词, | ||
| 214 | - * mix相同的概率非常低,提高效率 | ||
| 215 | - */ | ||
| 216 | - for(; node != null; node = node.next){ | ||
| 217 | - /* | ||
| 218 | - * 对于一个节点,先根据头2个字符判断是否属于这个节点。 | ||
| 219 | - * 如果属于这个节点,看这个节点的词库是否命中。 | ||
| 220 | - * 此代码块中访问次数已经很少,不是优化重点 | ||
| 221 | - */ | ||
| 222 | - if(node.headTwoCharMix == mix){ | ||
| 223 | - /* | ||
| 224 | - * 查出比剩余sentence小的最大的词。 | ||
| 225 | - * 例如剩余sentence为"色情电影哪家强?", | ||
| 226 | - * 这个节点含三个词从小到大为:“色情”、“色情电影”、“色情信息”。 | ||
| 227 | - * 则取到的word为“色情电影” | ||
| 228 | - */ | ||
| 229 | - StringPointer word = node.words.floor(sp.substring(i)); | ||
| 230 | - /* | ||
| 231 | - * 仍然需要再判断一次,例如“色情信息哪里有?”, | ||
| 232 | - * 如果节点只包含“色情电影”一个词, | ||
| 233 | - * 仍然能够取到word为“色情电影”,但是不该匹配。 | ||
| 234 | - */ | ||
| 235 | - if(word != null && sp.nextStartsWith(i, word)){ | ||
| 236 | - // 匹配成功,将匹配的部分,用replace制定的内容替代 | ||
| 237 | - sp.fill(i, i + word.length, replace); | ||
| 238 | - // 跳过已经替代的部分 | ||
| 239 | - step = word.length; | ||
| 240 | - // 标示有替换 | ||
| 241 | - replaced = true; | ||
| 242 | - // 跳出for循环(然后是while循环的下一个位置) | ||
| 243 | - break; | ||
| 244 | - } | ||
| 245 | - } | ||
| 246 | - } | ||
| 247 | - } | ||
| 248 | - | ||
| 249 | - // 移动到下一个匹配位置 | ||
| 250 | - i += step; | ||
| 251 | - } | ||
| 252 | - | ||
| 253 | - // 如果没有替换,直接返回入参(节约String的构造copy) | ||
| 254 | - if(replaced){ | ||
| 255 | - return sp.toString(); | ||
| 256 | - }else{ | ||
| 257 | - return sentence; | ||
| 258 | - } | ||
| 259 | - } | ||
| 260 | - | ||
| 261 | -} | ||
| 262 | -``` | ||
| 263 | - | ||
| 264 | -### SensitiveNode.java | ||
| 265 | - | ||
| 266 | - | ||
| 267 | -```java | ||
| 268 | -package com.odianyun.util.sensi; | ||
| 269 | - | ||
| 270 | -import java.io.Serializable; | ||
| 271 | -import java.util.TreeSet; | ||
| 272 | - | ||
| 273 | -/** | ||
| 274 | - * 敏感词节点,每个节点包含了以相同的2个字符开头的所有词 | ||
| 275 | - * | ||
| 276 | - * @author ZhangXiaoye | ||
| 277 | - * @date 2017年1月5日 下午5:06:26 | ||
| 278 | - */ | ||
| 279 | -public class SensitiveNode implements Serializable{ | ||
| 280 | - | ||
| 281 | - private static final long serialVersionUID = 1L; | ||
| 282 | - | ||
| 283 | - /** | ||
| 284 | - * 头两个字符的mix,mix相同,两个字符相同 | ||
| 285 | - */ | ||
| 286 | - protected final int headTwoCharMix; | ||
| 287 | - | ||
| 288 | - /** | ||
| 289 | - * 所有以这两个字符开头的词表 | ||
| 290 | - */ | ||
| 291 | - protected final TreeSet<StringPointer> words = new TreeSet<StringPointer>(); | ||
| 292 | - | ||
| 293 | - /** | ||
| 294 | - * 下一个节点 | ||
| 295 | - */ | ||
| 296 | - protected SensitiveNode next; | ||
| 297 | - | ||
| 298 | - public SensitiveNode(int headTwoCharMix){ | ||
| 299 | - this.headTwoCharMix = headTwoCharMix; | ||
| 300 | - } | ||
| 301 | - | ||
| 302 | - public SensitiveNode(int headTwoCharMix, SensitiveNode parent){ | ||
| 303 | - this.headTwoCharMix = headTwoCharMix; | ||
| 304 | - parent.next = this; | ||
| 305 | - } | ||
| 306 | - | ||
| 307 | -} | 51 | +然后,**在*********。 |
| 308 | ``` | 52 | ``` |
| 309 | 53 | ||
| 310 | -### StringPointer.java | ||
| 311 | - | ||
| 312 | -```java | ||
| 313 | -package com.odianyun.util.sensi; | ||
| 314 | - | ||
| 315 | -import java.io.Serializable; | ||
| 316 | -import java.util.HashMap; | ||
| 317 | -import java.util.TreeMap; | ||
| 318 | - | ||
| 319 | -/** | ||
| 320 | - * 没有注释的方法与{@link String}类似<br/> | ||
| 321 | - * <b>注意:</b>没有(数组越界等的)安全检查<br/> | ||
| 322 | - * 可以作为{@link HashMap}和{@link TreeMap}的key | ||
| 323 | - * | ||
| 324 | - * @author ZhangXiaoye | ||
| 325 | - * @date 2017年1月5日 下午2:11:56 | ||
| 326 | - */ | ||
| 327 | -public class StringPointer implements Serializable, CharSequence, Comparable<StringPointer>{ | ||
| 328 | - | ||
| 329 | - private static final long serialVersionUID = 1L; | ||
| 330 | - | ||
| 331 | - protected final char[] value; | ||
| 332 | - | ||
| 333 | - protected final int offset; | ||
| 334 | - | ||
| 335 | - protected final int length; | ||
| 336 | - | ||
| 337 | - private int hash = 0; | ||
| 338 | - | ||
| 339 | - public StringPointer(String str){ | ||
| 340 | - value = str.toCharArray(); | ||
| 341 | - offset = 0; | ||
| 342 | - length = value.length; | ||
| 343 | - } | 54 | +### 依赖 |
| 344 | 55 | ||
| 345 | - public StringPointer(char[] value, int offset, int length){ | ||
| 346 | - this.value = value; | ||
| 347 | - this.offset = offset; | ||
| 348 | - this.length = length; | ||
| 349 | - } | 56 | +JDK 1.7版本及以上 |
| 350 | 57 | ||
| 351 | - /** | ||
| 352 | - * 计算该位置后(包含)2个字符的hash值 | ||
| 353 | - * | ||
| 354 | - * @param i 从 0 到 length - 2 | ||
| 355 | - * @return hash值 | ||
| 356 | - * @author ZhangXiaoye | ||
| 357 | - * @date 2017年1月5日 下午2:23:02 | ||
| 358 | - */ | ||
| 359 | - public int nextTwoCharHash(int i){ | ||
| 360 | - return 31 * value[offset + i] + value[offset + i + 1]; | ||
| 361 | - } | ||
| 362 | - | ||
| 363 | - /** | ||
| 364 | - * 计算该位置后(包含)2个字符和为1个int型的值<br/> | ||
| 365 | - * int值相同表示2个字符相同 | ||
| 366 | - * | ||
| 367 | - * @param i 从 0 到 length - 2 | ||
| 368 | - * @return int值 | ||
| 369 | - * @author ZhangXiaoye | ||
| 370 | - * @date 2017年1月5日 下午2:46:58 | ||
| 371 | - */ | ||
| 372 | - public int nextTwoCharMix(int i){ | ||
| 373 | - return (value[offset + i] << 16) | value[offset + i + 1]; | ||
| 374 | - } | ||
| 375 | - | ||
| 376 | - /** | ||
| 377 | - * 该位置后(包含)的字符串,是否以某个词(word)开头 | ||
| 378 | - * | ||
| 379 | - * @param i 从 0 到 length - 2 | ||
| 380 | - * @param word 词 | ||
| 381 | - * @return 是否? | ||
| 382 | - * @author ZhangXiaoye | ||
| 383 | - * @date 2017年1月5日 下午3:13:49 | ||
| 384 | - */ | ||
| 385 | - public boolean nextStartsWith(int i, StringPointer word){ | ||
| 386 | - // 是否长度超出 | ||
| 387 | - if(word.length > length - i){ | ||
| 388 | - return false; | ||
| 389 | - } | ||
| 390 | - // 从尾开始判断 | ||
| 391 | - for(int c = word.length - 1; c >= 0; c --){ | ||
| 392 | - if(value[offset + i + c] != word.value[word.offset + c]){ | ||
| 393 | - return false; | ||
| 394 | - } | ||
| 395 | - } | ||
| 396 | - return true; | ||
| 397 | - } | ||
| 398 | - | ||
| 399 | - /** | ||
| 400 | - * 填充(替换) | ||
| 401 | - * | ||
| 402 | - * @param begin 从此位置开始(含) | ||
| 403 | - * @param end 到此位置结束(不含) | ||
| 404 | - * @param fillWith 以此字符填充(替换) | ||
| 405 | - * @author ZhangXiaoye | ||
| 406 | - * @date 2017年1月5日 下午3:29:21 | ||
| 407 | - */ | ||
| 408 | - public void fill(int begin, int end, char fillWith){ | ||
| 409 | - for(int i = begin; i < end; i ++){ | ||
| 410 | - value[offset + i] = fillWith; | ||
| 411 | - } | ||
| 412 | - } | ||
| 413 | - | ||
| 414 | - public int length(){ | ||
| 415 | - return length; | ||
| 416 | - } | ||
| 417 | - | ||
| 418 | - public char charAt(int i){ | ||
| 419 | - return value[offset + i]; | ||
| 420 | - } | ||
| 421 | - | ||
| 422 | - public StringPointer substring(int begin){ | ||
| 423 | - return new StringPointer(value, offset + begin, length - begin); | ||
| 424 | - } | ||
| 425 | - | ||
| 426 | - public StringPointer substring(int begin, int end){ | ||
| 427 | - return new StringPointer(value, offset + begin, end - begin); | ||
| 428 | - } | ||
| 429 | - | ||
| 430 | - @Override | ||
| 431 | - public CharSequence subSequence(int start, int end) { | ||
| 432 | - return substring(start, end); | ||
| 433 | - } | ||
| 434 | - | ||
| 435 | - public String toString(){ | ||
| 436 | - return new String(value, offset, length); | ||
| 437 | - } | ||
| 438 | - | ||
| 439 | - public int hashCode() { | ||
| 440 | - int h = hash; | ||
| 441 | - if (h == 0 && length > 0) { | ||
| 442 | - for (int i = 0; i < length; i++) { | ||
| 443 | - h = 31 * h + value[offset + i]; | ||
| 444 | - } | ||
| 445 | - hash = h; | ||
| 446 | - } | ||
| 447 | - return h; | ||
| 448 | - } | ||
| 449 | - | ||
| 450 | - public boolean equals(Object anObject) { | ||
| 451 | - if (this == anObject) { | ||
| 452 | - return true; | ||
| 453 | - } | ||
| 454 | - if (anObject instanceof StringPointer) { | ||
| 455 | - StringPointer that = (StringPointer)anObject; | ||
| 456 | - if (length == that.length) { | ||
| 457 | - char v1[] = this.value; | ||
| 458 | - char v2[] = that.value; | ||
| 459 | - for(int i = 0; i < this.length; i ++){ | ||
| 460 | - if(v1[this.offset + i] != v2[that.offset + i]){ | ||
| 461 | - return false; | ||
| 462 | - } | ||
| 463 | - } | ||
| 464 | - return true; | ||
| 465 | - } | ||
| 466 | - } | ||
| 467 | - return false; | ||
| 468 | - } | ||
| 469 | - | ||
| 470 | - @Override | ||
| 471 | - public int compareTo(StringPointer that) { | ||
| 472 | - int len1 = this.length; | ||
| 473 | - int len2 = that.length; | ||
| 474 | - int lim = Math.min(len1, len2); | ||
| 475 | - char v1[] = this.value; | ||
| 476 | - char v2[] = that.value; | ||
| 477 | - | ||
| 478 | - int k = 0; | ||
| 479 | - while (k < lim) { | ||
| 480 | - char c1 = v1[this.offset + k]; | ||
| 481 | - char c2 = v2[that.offset + k]; | ||
| 482 | - if (c1 != c2) { | ||
| 483 | - return c1 - c2; | ||
| 484 | - } | ||
| 485 | - k++; | ||
| 486 | - } | ||
| 487 | - return len1 - len2; | ||
| 488 | - } | ||
| 489 | - | ||
| 490 | -} | ||
| 491 | -``` | ||
| 492 | 58 |
| @@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
| 4 | 4 | ||
| 5 | <groupId>com.odianyun.util</groupId> | 5 | <groupId>com.odianyun.util</groupId> |
| 6 | <artifactId>sensitive-words</artifactId> | 6 | <artifactId>sensitive-words</artifactId> |
| 7 | - <version>1.0.1</version> | 7 | + <version>1.0.2</version> |
| 8 | <packaging>jar</packaging> | 8 | <packaging>jar</packaging> |
| 9 | 9 | ||
| 10 | <name>sensitive-words</name> | 10 | <name>sensitive-words</name> |
| @@ -5,6 +5,7 @@ import java.io.IOException; | @@ -5,6 +5,7 @@ import java.io.IOException; | ||
| 5 | import java.io.InputStreamReader; | 5 | import java.io.InputStreamReader; |
| 6 | import java.io.Serializable; | 6 | import java.io.Serializable; |
| 7 | import java.nio.charset.StandardCharsets; | 7 | import java.nio.charset.StandardCharsets; |
| 8 | +import java.util.NavigableSet; | ||
| 8 | 9 | ||
| 9 | /** | 10 | /** |
| 10 | * 敏感词过滤器,以过滤速度优化为主。<br/> | 11 | * 敏感词过滤器,以过滤速度优化为主。<br/> |
| @@ -80,9 +81,14 @@ public class SensitiveFilter implements Serializable{ | @@ -80,9 +81,14 @@ public class SensitiveFilter implements Serializable{ | ||
| 80 | * @author ZhangXiaoye | 81 | * @author ZhangXiaoye |
| 81 | * @date 2017年1月5日 下午2:35:21 | 82 | * @date 2017年1月5日 下午2:35:21 |
| 82 | */ | 83 | */ |
| 83 | - public void put(String word){ | 84 | + public boolean put(String word){ |
| 85 | + // 长度小于2的不加入 | ||
| 84 | if(word == null || word.trim().length() < 2){ | 86 | if(word == null || word.trim().length() < 2){ |
| 85 | - return; | 87 | + return false; |
| 88 | + } | ||
| 89 | + // 两个字符的不考虑 | ||
| 90 | + if(word.length() == 2 && word.matches("\\w\\w")){ | ||
| 91 | + return false; | ||
| 86 | } | 92 | } |
| 87 | StringPointer sp = new StringPointer(word.trim()); | 93 | StringPointer sp = new StringPointer(word.trim()); |
| 88 | // 计算头两个字符的hash | 94 | // 计算头两个字符的hash |
| @@ -107,19 +113,26 @@ public class SensitiveFilter implements Serializable{ | @@ -107,19 +113,26 @@ public class SensitiveFilter implements Serializable{ | ||
| 107 | // 匹配节点 | 113 | // 匹配节点 |
| 108 | if(node.headTwoCharMix == mix){ | 114 | if(node.headTwoCharMix == mix){ |
| 109 | node.words.add(sp); | 115 | node.words.add(sp); |
| 110 | - return; | 116 | + return true; |
| 111 | } | 117 | } |
| 112 | // 如果匹配到最后仍然不成功,则追加一个节点 | 118 | // 如果匹配到最后仍然不成功,则追加一个节点 |
| 113 | if(node.next == null){ | 119 | if(node.next == null){ |
| 114 | new SensitiveNode(mix, node).words.add(sp); | 120 | new SensitiveNode(mix, node).words.add(sp); |
| 115 | - return; | 121 | + return true; |
| 116 | } | 122 | } |
| 117 | } | 123 | } |
| 118 | } | 124 | } |
| 125 | + return true; | ||
| 119 | } | 126 | } |
| 120 | 127 | ||
| 121 | /** | 128 | /** |
| 122 | - * 对句子进行敏感词过滤 | 129 | + * 对句子进行敏感词过滤<br/> |
| 130 | + * 如果无敏感词返回输入的sentence对象,即可以用下面的方式判断是否有敏感词:<br/><code> | ||
| 131 | + * String result = filter.filter(sentence, '*');<br/> | ||
| 132 | + * if(result != sentence){<br/> | ||
| 133 | + * // 有敏感词<br/> | ||
| 134 | + * } | ||
| 135 | + * </code> | ||
| 123 | * | 136 | * |
| 124 | * @param sentence 句子 | 137 | * @param sentence 句子 |
| 125 | * @param replace 敏感词的替换字符 | 138 | * @param replace 敏感词的替换字符 |
| @@ -165,6 +178,7 @@ public class SensitiveFilter implements Serializable{ | @@ -165,6 +178,7 @@ public class SensitiveFilter implements Serializable{ | ||
| 165 | * 循环所有的节点,如果非敏感词, | 178 | * 循环所有的节点,如果非敏感词, |
| 166 | * mix相同的概率非常低,提高效率 | 179 | * mix相同的概率非常低,提高效率 |
| 167 | */ | 180 | */ |
| 181 | + outer: | ||
| 168 | for(; node != null; node = node.next){ | 182 | for(; node != null; node = node.next){ |
| 169 | /* | 183 | /* |
| 170 | * 对于一个节点,先根据头2个字符判断是否属于这个节点。 | 184 | * 对于一个节点,先根据头2个字符判断是否属于这个节点。 |
| @@ -175,26 +189,31 @@ public class SensitiveFilter implements Serializable{ | @@ -175,26 +189,31 @@ public class SensitiveFilter implements Serializable{ | ||
| 175 | /* | 189 | /* |
| 176 | * 查出比剩余sentence小的最大的词。 | 190 | * 查出比剩余sentence小的最大的词。 |
| 177 | * 例如剩余sentence为"色情电影哪家强?", | 191 | * 例如剩余sentence为"色情电影哪家强?", |
| 178 | - * 这个节点含三个词从小到大为:“色情”、“色情电影”、“色情信息”。 | ||
| 179 | - * 则取到的word为“色情电影” | 192 | + * 这个节点含三个词从小到大为:"色情"、"色情电影"、"色情信息"。 |
| 193 | + * 则从“色情电影”开始向前匹配 | ||
| 180 | */ | 194 | */ |
| 181 | - StringPointer word = node.words.floor(sp.substring(i)); | 195 | + NavigableSet<StringPointer> desSet = node.words.headSet(sp.substring(i), true); |
| 196 | + if(desSet != null){ | ||
| 197 | + for(StringPointer word: desSet){ | ||
| 182 | /* | 198 | /* |
| 183 | - * 仍然需要再判断一次,例如“色情信息哪里有?”, | ||
| 184 | - * 如果节点只包含“色情电影”一个词, | ||
| 185 | - * 仍然能够取到word为“色情电影”,但是不该匹配。 | 199 | + * 仍然需要再判断一次,例如"色情信息哪里有?", |
| 200 | + * 如果节点只包含"色情电影"一个词, | ||
| 201 | + * 仍然能够取到word为"色情电影",但是不该匹配。 | ||
| 186 | */ | 202 | */ |
| 187 | - if(word != null && sp.nextStartsWith(i, word)){ | 203 | + if(sp.nextStartsWith(i, word)){ |
| 188 | // 匹配成功,将匹配的部分,用replace制定的内容替代 | 204 | // 匹配成功,将匹配的部分,用replace制定的内容替代 |
| 189 | sp.fill(i, i + word.length, replace); | 205 | sp.fill(i, i + word.length, replace); |
| 190 | // 跳过已经替代的部分 | 206 | // 跳过已经替代的部分 |
| 191 | step = word.length; | 207 | step = word.length; |
| 192 | // 标示有替换 | 208 | // 标示有替换 |
| 193 | replaced = true; | 209 | replaced = true; |
| 194 | - // 跳出for循环(然后是while循环的下一个位置) | ||
| 195 | - break; | 210 | + // 跳出循环(然后是while循环的下一个位置) |
| 211 | + break outer; | ||
| 212 | + } | ||
| 196 | } | 213 | } |
| 197 | } | 214 | } |
| 215 | + | ||
| 216 | + } | ||
| 198 | } | 217 | } |
| 199 | } | 218 | } |
| 200 | 219 |
| @@ -529,7 +529,6 @@ pussy | @@ -529,7 +529,6 @@ pussy | ||
| 529 | 遊行 | 529 | 遊行 |
| 530 | 偷电 | 530 | 偷电 |
| 531 | 龟头粗壮赤红 | 531 | 龟头粗壮赤红 |
| 532 | -管理 | ||
| 533 | 淫荡女医生穴 | 532 | 淫荡女医生穴 |
| 534 | 官商勾结 | 533 | 官商勾结 |
| 535 | 小鸡巴 | 534 | 小鸡巴 |
| @@ -1597,7 +1596,6 @@ orgasmus | @@ -1597,7 +1596,6 @@ orgasmus | ||
| 1597 | 黄蓉舔小龙女的乳头 | 1596 | 黄蓉舔小龙女的乳头 |
| 1598 | 色情網站 | 1597 | 色情網站 |
| 1599 | 叔嫂肉欲 | 1598 | 叔嫂肉欲 |
| 1600 | -飘飘 | ||
| 1601 | 塞白 | 1599 | 塞白 |
| 1602 | 眯眯爱大型黄站 | 1600 | 眯眯爱大型黄站 |
| 1603 | 妹妹陰毛 | 1601 | 妹妹陰毛 |
| @@ -1724,7 +1722,6 @@ sb | @@ -1724,7 +1722,6 @@ sb | ||
| 1724 | 日本素人 | 1722 | 日本素人 |
| 1725 | 午夜激情美穴贴台湾性福联盟 | 1723 | 午夜激情美穴贴台湾性福联盟 |
| 1726 | 手记注册日韩5006部激情电影 | 1724 | 手记注册日韩5006部激情电影 |
| 1727 | -ro | ||
| 1728 | 全國人大 | 1725 | 全國人大 |
| 1729 | 蘇秀文 | 1726 | 蘇秀文 |
| 1730 | 诛仙 | 1727 | 诛仙 |
| @@ -2148,7 +2145,6 @@ fuck外专局 | @@ -2148,7 +2145,6 @@ fuck外专局 | ||
| 2148 | 教你怎样延迟射精34岁以下禁免费淫电影a片 | 2145 | 教你怎样延迟射精34岁以下禁免费淫电影a片 |
| 2149 | 干他 | 2146 | 干他 |
| 2150 | 女幹三虧 | 2147 | 女幹三虧 |
| 2151 | -妹妹 | ||
| 2152 | 中国之春 | 2148 | 中国之春 |
| 2153 | 超美女优MUGEN EX Vol。 7~浜崎ひめ | 2149 | 超美女优MUGEN EX Vol。 7~浜崎ひめ |
| 2154 | transsexuell | 2150 | transsexuell |
| @@ -2220,7 +2216,6 @@ fuck私服 | @@ -2220,7 +2216,6 @@ fuck私服 | ||
| 2220 | g。m | 2216 | g。m |
| 2221 | 毛叫小姐 | 2217 | 毛叫小姐 |
| 2222 | meinv嫩穴 妹穴 亮穴 女人穴 | 2218 | meinv嫩穴 妹穴 亮穴 女人穴 |
| 2223 | -母亲 | ||
| 2224 | 北极冰 | 2219 | 北极冰 |
| 2225 | 誘姦 | 2220 | 誘姦 |
| 2226 | mabinogi | 2221 | mabinogi |
| @@ -2400,7 +2395,6 @@ yifeng | @@ -2400,7 +2395,6 @@ yifeng | ||
| 2400 | 巨奶 | 2395 | 巨奶 |
| 2401 | 好嫩 | 2396 | 好嫩 |
| 2402 | 女尔 | 2397 | 女尔 |
| 2403 | -妈妈 | ||
| 2404 | 鰢 | 2398 | 鰢 |
| 2405 | SM援交 | 2399 | SM援交 |
| 2406 | www。ccwcc。cn | 2400 | www。ccwcc。cn |
| @@ -6018,8 +6012,6 @@ meimei的嫩阴户 | @@ -6018,8 +6012,6 @@ meimei的嫩阴户 | ||
| 6018 | 新手輔導員 | 6012 | 新手輔導員 |
| 6019 | Tora Tora Gold Vol。15 香山圣 | 6013 | Tora Tora Gold Vol。15 香山圣 |
| 6020 | 海洛因 | 6014 | 海洛因 |
| 6021 | -杨帆 | ||
| 6022 | -温馨 | ||
| 6023 | 毛他母亲 | 6015 | 毛他母亲 |
| 6024 | meinv极品嫩穴 | 6016 | meinv极品嫩穴 |
| 6025 | 龟头顶破了处女膜 | 6017 | 龟头顶破了处女膜 |
| @@ -6633,7 +6625,6 @@ cdjp李鹏* | @@ -6633,7 +6625,6 @@ cdjp李鹏* | ||
| 6633 | 色情 导航 | 6625 | 色情 导航 |
| 6634 | 三民主义 | 6626 | 三民主义 |
| 6635 | 毛阴茎 | 6627 | 毛阴茎 |
| 6636 | -公告消息 | ||
| 6637 | 私¥服 | 6628 | 私¥服 |
| 6638 | 毛一国两制 | 6629 | 毛一国两制 |
| 6639 | 和两勇男玩15P | 6630 | 和两勇男玩15P |
| @@ -6713,7 +6704,6 @@ mland | @@ -6713,7 +6704,6 @@ mland | ||
| 6713 | 你爺 | 6704 | 你爺 |
| 6714 | fuck尉健行 | 6705 | fuck尉健行 |
| 6715 | 迷魂药 | 6706 | 迷魂药 |
| 6716 | -你爸 | ||
| 6717 | mai骚 | 6707 | mai骚 |
| 6718 | 科洛斯 | 6708 | 科洛斯 |
| 6719 | 淫色俱乐部 | 6709 | 淫色俱乐部 |
| @@ -7240,7 +7230,6 @@ Red Hot Fetish 纹舞らん--スケパン刑事 | @@ -7240,7 +7230,6 @@ Red Hot Fetish 纹舞らん--スケパン刑事 | ||
| 7240 | 援助交易 | 7230 | 援助交易 |
| 7241 | 23dy淫色贴图 | 7231 | 23dy淫色贴图 |
| 7242 | 吳邦囯 | 7232 | 吳邦囯 |
| 7243 | -爷爷 | ||
| 7244 | 自拍 | 7233 | 自拍 |
| 7245 | 法~~輪 | 7234 | 法~~輪 |
| 7246 | BlowJobs | 7235 | BlowJobs |
| @@ -7259,7 +7248,6 @@ fuck投毒杀人 | @@ -7259,7 +7248,6 @@ fuck投毒杀人 | ||
| 7259 | 激情玩穴网 | 7248 | 激情玩穴网 |
| 7260 | 妹妹的嫩阴唇 | 7249 | 妹妹的嫩阴唇 |
| 7261 | butthole | 7250 | butthole |
| 7262 | -爸爸 | ||
| 7263 | 毛妳老母的 | 7251 | 毛妳老母的 |
| 7264 | 管理员 | 7252 | 管理员 |
| 7265 | 金酷 | 7253 | 金酷 |
| @@ -8048,7 +8036,6 @@ hardcore | @@ -8048,7 +8036,6 @@ hardcore | ||
| 8048 | 中功 | 8036 | 中功 |
| 8049 | 精品性交图 | 8037 | 精品性交图 |
| 8050 | 瑞士金融大学 | 8038 | 瑞士金融大学 |
| 8051 | -系统消息 | ||
| 8052 | 邻居美少妇 操淫荡少妇 | 8039 | 邻居美少妇 操淫荡少妇 |
| 8053 | 成人色情导航 | 8040 | 成人色情导航 |
| 8054 | 完全免费在线淫电影 看完保证你打飞机 | 8041 | 完全免费在线淫电影 看完保证你打飞机 |
| @@ -9973,7 +9960,6 @@ tranny | @@ -9973,7 +9960,6 @@ tranny | ||
| 9973 | 我讓朋友強姦我的小姨子 | 9960 | 我讓朋友強姦我的小姨子 |
| 9974 | 大城舞衣子(有碼無碼)大合輯~~~超優!!(可分開下載哦!!!) | 9961 | 大城舞衣子(有碼無碼)大合輯~~~超優!!(可分開下載哦!!!) |
| 9975 | 操妳娘 | 9962 | 操妳娘 |
| 9976 | -通知 | ||
| 9977 | 合成淫色贴图 | 9963 | 合成淫色贴图 |
| 9978 | Qqyinsu | 9964 | Qqyinsu |
| 9979 | fuck晚年周恩来 | 9965 | fuck晚年周恩来 |
| @@ -10319,7 +10305,6 @@ fuck统计局 | @@ -10319,7 +10305,6 @@ fuck统计局 | ||
| 10319 | 欲仙欲死 | 10305 | 欲仙欲死 |
| 10320 | 干你媽逼 | 10306 | 干你媽逼 |
| 10321 | 淫奸护士穴论坛 | 10307 | 淫奸护士穴论坛 |
| 10322 | -好朋友 | ||
| 10323 | 身份生成器 | 10308 | 身份生成器 |
| 10324 | 法@@倫 | 10309 | 法@@倫 |
| 10325 | 欧洲男女激情淫乱穴 mm贴图网 | 10310 | 欧洲男女激情淫乱穴 mm贴图网 |
| @@ -10714,7 +10699,6 @@ qi shi | @@ -10714,7 +10699,6 @@ qi shi | ||
| 10714 | 新人调教 Red Hot Jam Vol。9 Myuu | 10699 | 新人调教 Red Hot Jam Vol。9 Myuu |
| 10715 | 毛奸你 | 10700 | 毛奸你 |
| 10716 | 恐怖主义 | 10701 | 恐怖主义 |
| 10717 | -领导 | ||
| 10718 | 勁樂 | 10702 | 勁樂 |
| 10719 | 性爱插穴 | 10703 | 性爱插穴 |
| 10720 | 你呀的鸡巴长歪了吧 | 10704 | 你呀的鸡巴长歪了吧 |
| @@ -11226,7 +11210,6 @@ a 片成人免费激情小电影 | @@ -11226,7 +11210,6 @@ a 片成人免费激情小电影 | ||
| 11226 | 真三国无双OL | 11210 | 真三国无双OL |
| 11227 | 陈慕华 | 11211 | 陈慕华 |
| 11228 | 真孔雀王 | 11212 | 真孔雀王 |
| 11229 | -消息 | ||
| 11230 | yeeplay | 11213 | yeeplay |
| 11231 | 操 | 11214 | 操 |
| 11232 | 东突 | 11215 | 东突 |
| @@ -11405,7 +11388,6 @@ meimei的嫩屄 | @@ -11405,7 +11388,6 @@ meimei的嫩屄 | ||
| 11405 | jiejie被轮奸 | 11388 | jiejie被轮奸 |
| 11406 | 18dy免费观看 | 11389 | 18dy免费观看 |
| 11407 | 免费放淫电影**日本高校女生性交 | 11390 | 免费放淫电影**日本高校女生性交 |
| 11408 | -销售 | ||
| 11409 | 好紧的屄啊 | 11391 | 好紧的屄啊 |
| 11410 | 祁建 | 11392 | 祁建 |
| 11411 | 18岁少女处夜激情性爱电影下载中心 | 11393 | 18岁少女处夜激情性爱电影下载中心 |
| @@ -12004,7 +11986,6 @@ fofg | @@ -12004,7 +11986,6 @@ fofg | ||
| 12004 | 鷄巴 | 11986 | 鷄巴 |
| 12005 | 手记注册日韩5002部激情电影 | 11987 | 手记注册日韩5002部激情电影 |
| 12006 | 干死你 | 11988 | 干死你 |
| 12007 | -服务管理 | ||
| 12008 | 渡口 | 11989 | 渡口 |
| 12009 | 毛回良玉 | 11990 | 毛回良玉 |
| 12010 | 又黑又粗长的鸡巴 | 11991 | 又黑又粗长的鸡巴 |
| @@ -12102,7 +12083,6 @@ EVENT | @@ -12102,7 +12083,6 @@ EVENT | ||
| 12102 | 淫奴 | 12083 | 淫奴 |
| 12103 | 法陯功 | 12084 | 法陯功 |
| 12104 | 淫女 | 12085 | 淫女 |
| 12105 | -锤子 | ||
| 12106 | 淫奸 | 12086 | 淫奸 |
| 12107 | 钱 其 琛 | 12087 | 钱 其 琛 |
| 12108 | 龟头插进 | 12088 | 龟头插进 |
| @@ -12304,7 +12284,6 @@ roiworld | @@ -12304,7 +12284,6 @@ roiworld | ||
| 12304 | 淪 | 12284 | 淪 |
| 12305 | 肏妹妹的小骚屄 | 12285 | 肏妹妹的小骚屄 |
| 12306 | 强*十八岁meinv护士看完保证你打飞机免费淫电影a片 | 12286 | 强*十八岁meinv护士看完保证你打飞机免费淫电影a片 |
| 12307 | -系统管理员 | ||
| 12308 | 在线淫色电影 | 12287 | 在线淫色电影 |
| 12309 | 天安门英雄纪念碑 | 12288 | 天安门英雄纪念碑 |
| 12310 | 儿子添着妈妈肥大湿润阴唇 | 12289 | 儿子添着妈妈肥大湿润阴唇 |
| @@ -12694,7 +12673,6 @@ fuck私/服 | @@ -12694,7 +12673,6 @@ fuck私/服 | ||
| 12694 | Tokyo Hot n0232 : 真宫梨沙子 | 12673 | Tokyo Hot n0232 : 真宫梨沙子 |
| 12695 | 西哈努克 | 12674 | 西哈努克 |
| 12696 | 莎士比亚 | 12675 | 莎士比亚 |
| 12697 | -明慧 | ||
| 12698 | 台灣民国 | 12676 | 台灣民国 |
| 12699 | 硕大的龟头 | 12677 | 硕大的龟头 |
| 12700 | 黄 菊 | 12678 | 黄 菊 |
| @@ -12734,7 +12712,6 @@ Lateinos | @@ -12734,7 +12712,6 @@ Lateinos | ||
| 12734 | 街上钓个漂亮马子回家玩 | 12712 | 街上钓个漂亮马子回家玩 |
| 12735 | meinv主持25小时全天激情热舞 | 12713 | meinv主持25小时全天激情热舞 |
| 12736 | 輿論鉗制 | 12714 | 輿論鉗制 |
| 12737 | -服務管理 | ||
| 12738 | 蒋正华 | 12715 | 蒋正华 |
| 12739 | 遭警察殴打 | 12716 | 遭警察殴打 |
| 12740 | 本拉丹 | 12717 | 本拉丹 |
| @@ -13340,7 +13317,6 @@ sex大型色站 | @@ -13340,7 +13317,6 @@ sex大型色站 | ||
| 13340 | 抽插38岁处女妹妹淫叫不断 | 13317 | 抽插38岁处女妹妹淫叫不断 |
| 13341 | 激爱贴图 加西激情中文网 | 13318 | 激爱贴图 加西激情中文网 |
| 13342 | 女性私处锻炼法 | 13319 | 女性私处锻炼法 |
| 13343 | -哥哥 | ||
| 13344 | 操 | 13320 | 操 |
| 13345 | 我的穴 | 13321 | 我的穴 |
| 13346 | 税力 | 13322 | 税力 |
| @@ -14270,8 +14246,6 @@ ur靠 | @@ -14270,8 +14246,6 @@ ur靠 | ||
| 14270 | 劉曉竹 | 14246 | 劉曉竹 |
| 14271 | 免费淫奸女A片段 免费在线观看 | 14247 | 免费淫奸女A片段 免费在线观看 |
| 14272 | 习近平 | 14248 | 习近平 |
| 14273 | -姐姐 | ||
| 14274 | -的 | ||
| 14275 | 嫩穴 | 14249 | 嫩穴 |
| 14276 | 淫液 | 14250 | 淫液 |
| 14277 | 操大波波 | 14251 | 操大波波 |
| @@ -14353,7 +14327,6 @@ fuck特别公告 | @@ -14353,7 +14327,6 @@ fuck特别公告 | ||
| 14353 | 鹿城色meimei大型黄站 | 14327 | 鹿城色meimei大型黄站 |
| 14354 | 揭批书 | 14328 | 揭批书 |
| 14355 | 互淫 | 14329 | 互淫 |
| 14356 | -管理人员 | ||
| 14357 | 少妇白洁的淫乱生活 | 14330 | 少妇白洁的淫乱生活 |
| 14358 | 真理組織 | 14331 | 真理組織 |
| 14359 | 丁石孫 | 14332 | 丁石孫 |
| @@ -14,9 +14,21 @@ public class SensitiveFilterTest extends TestCase{ | @@ -14,9 +14,21 @@ public class SensitiveFilterTest extends TestCase{ | ||
| 14 | 14 | ||
| 15 | public void test() throws Exception{ | 15 | public void test() throws Exception{ |
| 16 | 16 | ||
| 17 | + // 使用默认单例(加载默认词典) | ||
| 17 | SensitiveFilter filter = SensitiveFilter.DEFAULT; | 18 | SensitiveFilter filter = SensitiveFilter.DEFAULT; |
| 18 | - | ||
| 19 | - System.out.println(filter.filter("会上,主席进行了发言。", '*')); | 19 | + // 向过滤器增加一个词 |
| 20 | + filter.put("婚礼上唱春天在哪里"); | ||
| 21 | + | ||
| 22 | + // 待过滤的句子 | ||
| 23 | + String sentence = "然后,市长在婚礼上唱春天在哪里。"; | ||
| 24 | + // 进行过滤 | ||
| 25 | + String filted = filter.filter(sentence, '*'); | ||
| 26 | + | ||
| 27 | + // 如果未过滤,则返回输入的String引用 | ||
| 28 | + if(sentence != filted){ | ||
| 29 | + // 句子中有敏感词 | ||
| 30 | + System.out.println(filted); | ||
| 31 | + } | ||
| 20 | 32 | ||
| 21 | } | 33 | } |
| 22 | 34 | ||
| @@ -42,24 +54,22 @@ public class SensitiveFilterTest extends TestCase{ | @@ -42,24 +54,22 @@ public class SensitiveFilterTest extends TestCase{ | ||
| 42 | } | 54 | } |
| 43 | } | 55 | } |
| 44 | 56 | ||
| 45 | - System.out.println(String.format("共加载 %d 行,%d 字符。", testSuit.size(), length)); | 57 | + System.out.println(String.format("待过滤文本共 %d 行,%d 字符。", testSuit.size(), length)); |
| 46 | 58 | ||
| 47 | 59 | ||
| 48 | SensitiveFilter filter = SensitiveFilter.DEFAULT; | 60 | SensitiveFilter filter = SensitiveFilter.DEFAULT; |
| 49 | 61 | ||
| 50 | int replaced = 0; | 62 | int replaced = 0; |
| 51 | 63 | ||
| 52 | - for(String line: testSuit){ | ||
| 53 | - if(! line.contains("`")){ | ||
| 54 | - String result = filter.filter(line, '`'); | ||
| 55 | - if(result.contains("`")){ | ||
| 56 | - ps.println(line); | 64 | + for(String sentence: testSuit){ |
| 65 | + String result = filter.filter(sentence, '*'); | ||
| 66 | + if(result != sentence){ | ||
| 67 | + ps.println(sentence); | ||
| 57 | ps.println(result); | 68 | ps.println(result); |
| 58 | ps.println(); | 69 | ps.println(); |
| 59 | replaced ++; | 70 | replaced ++; |
| 60 | } | 71 | } |
| 61 | } | 72 | } |
| 62 | - } | ||
| 63 | ps.close(); | 73 | ps.close(); |
| 64 | 74 | ||
| 65 | long timer = System.currentTimeMillis(); | 75 | long timer = System.currentTimeMillis(); |
| @@ -67,7 +77,7 @@ public class SensitiveFilterTest extends TestCase{ | @@ -67,7 +77,7 @@ public class SensitiveFilterTest extends TestCase{ | ||
| 67 | filter.filter(line, '*'); | 77 | filter.filter(line, '*'); |
| 68 | } | 78 | } |
| 69 | timer = System.currentTimeMillis() - timer; | 79 | timer = System.currentTimeMillis() - timer; |
| 70 | - System.out.println(String.format("共耗时 %1.3f 秒, 速度为 %1.1f字符/毫秒", timer * 1E-3, length / (double) timer)); | 80 | + System.out.println(String.format("过滤耗时 %1.3f 秒, 速度为 %1.1f字符/毫秒", timer * 1E-3, length / (double) timer)); |
| 71 | System.out.println(String.format("其中 %d 行有替换", replaced)); | 81 | System.out.println(String.format("其中 %d 行有替换", replaced)); |
| 72 | 82 | ||
| 73 | } | 83 | } |
-
请 注册 或 登录 后发表评论