OpenSource - 基于 DFA 算法实现的高性能 java 敏感词过滤工具框架

文章目录

  • sensitive-word
    • 创作目的
    • 特性
    • 变更日志
    • 更多资料
      • 敏感词控台
      • 敏感词标签文件
  • 快速开始
    • 准备
    • Maven 引入
    • 核心方法
      • 判断是否包含敏感词
      • 返回第一个敏感词
      • 返回所有敏感词
      • 默认的替换策略
      • 指定替换的内容
      • 自定义替换策略
    • IWordResultHandler 结果处理类
      • 使用实例
  • 更多特性
    • 样式处理
      • 忽略大小写
      • 忽略半角圆角
      • 忽略数字的写法
      • 忽略繁简体
      • 忽略英文的书写格式
      • 忽略重复词
    • 更多检测策略
      • 邮箱检测
      • 连续数字检测
      • 网址检测
  • 引导类特性配置
    • 说明
    • 配置方法
    • 配置说明
  • 忽略字符
    • 说明
    • 例子
  • 敏感词标签
    • 说明
    • 入门例子
      • 接口
      • 配置文件
      • 实现
  • 动态加载(用户自定义)
    • 情景说明
    • 接口说明
      • IWordDeny
      • IWordAllow
    • 配置使用
      • 系统的默认配置
      • 指定自己的实现
      • 同时配置多个
  • spring 整合
    • 背景
    • 自定义数据源
    • 动态变更
  • Benchmark
    • 环境
    • 测试效果记录
  • 后期 road-map
  • 拓展阅读
  • NLP 开源矩阵

在这里插入图片描述


sensitive-word

sensitive-word 基于 DFA 算法实现的高性能敏感词工具。

The sensitive word tool for java.(敏感词/违禁词/违法词/脏词。基于 DFA 算法实现的高性能 java 敏感词过滤工具框架。请勿发布涉及政治、广告、营销、翻墙、违反国家法律法规等内容。高性能敏感词检测过滤组件,附带繁体简体互换,支持全角半角互换,汉字转拼音,模糊搜索等功能。)

在这里插入图片描述

在线体验

创作目的

实现一款好用敏感词工具。

基于 DFA 算法实现,目前敏感词库内容收录 6W+(源文件 18W+,经过一次删减)。

后期将进行持续优化和补充敏感词库,并进一步提升算法的性能。

希望可以细化敏感词的分类,感觉工作量比较大,暂时没有进行。

特性

  • 6W+ 词库,且不断优化更新

  • 基于 fluent-api 实现,使用优雅简洁

  • 基于 DFA 算法,性能为 7W+ QPS,应用无感

  • 支持敏感词的判断、返回、脱敏等常见操作

  • 支持常见的格式转换

全角半角互换、英文大小写互换、数字常见形式的互换、中文繁简体互换、英文常见形式的互换、忽略重复词等

  • 支持敏感词检测、邮箱检测、数字检测、网址检测等

  • 支持自定义替换策略

  • 支持用户自定义敏感词和白名单

  • 支持数据的数据动态更新(用户自定义),实时生效

  • 支持敏感词的标签接口

  • 支持跳过一些特殊字符,让匹配更灵活

变更日志

CHANGE_LOG.md

更多资料

敏感词控台

有时候敏感词有一个控台,配置起来会更加灵活方便。

java 如何实现开箱即用的敏感词控台服务?

敏感词标签文件

梳理了大量的敏感词标签文件,可以让我们的敏感词更加方便。

这两个资料阅读可在下方文章获取:

v0.11.0-敏感词新特性及对应标签文件

快速开始

准备

  • JDK1.7+

  • Maven 3.x+

Maven 引入

<dependency><groupId>com.github.houbb</groupId><artifactId>sensitive-word</artifactId><version>0.12.0</version>
</dependency>

核心方法

SensitiveWordHelper 作为敏感词的工具类,核心方法如下:

方法参数返回值说明
contains(String)待验证的字符串布尔值验证字符串是否包含敏感词
replace(String, ISensitiveWordReplace)使用指定的替换策略替换敏感词字符串返回脱敏后的字符串
replace(String, char)使用指定的 char 替换敏感词字符串返回脱敏后的字符串
replace(String)使用 * 替换敏感词字符串返回脱敏后的字符串
findAll(String)待验证的字符串字符串列表返回字符串中所有敏感词
findFirst(String)待验证的字符串字符串返回字符串中第一个敏感词
findAll(String, IWordResultHandler)IWordResultHandler 结果处理类字符串列表返回字符串中所有敏感词
findFirst(String, IWordResultHandler)IWordResultHandler 结果处理类字符串返回字符串中第一个敏感词
tags(String)获取敏感词的标签敏感词字符串返回敏感词的标签列表

判断是否包含敏感词

final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。";Assert.assertTrue(SensitiveWordHelper.contains(text));

返回第一个敏感词

final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。";String word = SensitiveWordHelper.findFirst(text);
Assert.assertEquals("五星红旗", word);

SensitiveWordHelper.findFirst(text) 等价于:

String word = SensitiveWordHelper.findFirst(text, WordResultHandlers.word());

WordResultHandlers.raw() 可以保留对应的下标信息:

final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。";IWordResult word = SensitiveWordHelper.findFirst(text, WordResultHandlers.raw());
Assert.assertEquals("WordResult{startIndex=0, endIndex=4}", word.toString());

返回所有敏感词

final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。";List<String> wordList = SensitiveWordHelper.findAll(text);
Assert.assertEquals("[五星红旗, 毛主席, 天安门]", wordList.toString());

返回所有敏感词用法上类似于 SensitiveWordHelper.findFirst(),同样也支持指定结果处理类。

SensitiveWordHelper.findAll(text) 等价于:

List<String> wordList = SensitiveWordHelper.findAll(text, WordResultHandlers.word());

WordResultHandlers.raw() 可以保留对应的下标信息:

final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。";List<IWordResult> wordList = SensitiveWordHelper.findAll(text, WordResultHandlers.raw());
Assert.assertEquals("[WordResult{startIndex=0, endIndex=4}, WordResult{startIndex=9, endIndex=12}, WordResult{startIndex=18, endIndex=21}]", wordList.toString());

默认的替换策略

final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。";
String result = SensitiveWordHelper.replace(text);
Assert.assertEquals("****迎风飘扬,***的画像屹立在***前。", result);

指定替换的内容

final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。";
String result = SensitiveWordHelper.replace(text, '0');
Assert.assertEquals("0000迎风飘扬,000的画像屹立在000前。", result);

自定义替换策略

V0.2.0 支持该特性。

场景说明:有时候我们希望不同的敏感词有不同的替换结果。比如【游戏】替换为【电子竞技】,【失业】替换为【灵活就业】。

诚然,提前使用字符串的正则替换也可以,不过性能一般。

使用例子:

/*** 自定替换策略* @since 0.2.0*/
@Test
public void defineReplaceTest() {final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。";ISensitiveWordReplace replace = new MySensitiveWordReplace();String result = SensitiveWordHelper.replace(text, replace);Assert.assertEquals("国家旗帜迎风飘扬,教员的画像屹立在***前。", result);
}

其中 MySensitiveWordReplace 是我们自定义的替换策略,实现如下:

public class MyWordReplace implements IWordReplace {@Overridepublic void replace(StringBuilder stringBuilder, final char[] rawChars, IWordResult wordResult, IWordContext wordContext) {String sensitiveWord = InnerWordCharUtils.getString(rawChars, wordResult);// 自定义不同的敏感词替换策略,可以从数据库等地方读取if("五星红旗".equals(sensitiveWord)) {stringBuilder.append("国家旗帜");} else if("毛主席".equals(sensitiveWord)) {stringBuilder.append("教员");} else {// 其他默认使用 * 代替int wordLength = wordResult.endIndex() - wordResult.startIndex();for(int i = 0; i < wordLength; i++) {stringBuilder.append('*');}}}}

我们针对其中的部分词做固定映射处理,其他的默认转换为 *

IWordResultHandler 结果处理类

IWordResultHandler 可以对敏感词的结果进行处理,允许用户自定义。

内置实现见 WordResultHandlers 工具类:

  • WordResultHandlers.word()

只保留敏感词单词本身。

  • WordResultHandlers.raw()

保留敏感词相关信息,包含敏感词的开始和结束下标。

  • WordResultHandlers.wordTags()

同时保留单词,和对应的词标签信息。

使用实例

所有测试案例参见 SensitiveWordHelperTest

1)基本例子

final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。";List<String> wordList = SensitiveWordHelper.findAll(text);
Assert.assertEquals("[五星红旗, 毛主席, 天安门]", wordList.toString());
List<String> wordList2 = SensitiveWordHelper.findAll(text, WordResultHandlers.word());
Assert.assertEquals("[五星红旗, 毛主席, 天安门]", wordList2.toString());List<IWordResult> wordList3 = SensitiveWordHelper.findAll(text, WordResultHandlers.raw());
Assert.assertEquals("[WordResult{startIndex=0, endIndex=4}, WordResult{startIndex=9, endIndex=12}, WordResult{startIndex=18, endIndex=21}]", wordList3.toString());
  1. wordTags 例子

我们在 dict_tag_test.txt 文件中指定对应词的标签信息。

final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。";// 默认敏感词标签为空
List<WordTagsDto> wordList1 = SensitiveWordHelper.findAll(text, WordResultHandlers.wordTags());
Assert.assertEquals("[WordTagsDto{word='五星红旗', tags=[]}, WordTagsDto{word='毛主席', tags=[]}, WordTagsDto{word='天安门', tags=[]}]", wordList1.toString());List<WordTagsDto> wordList2 = SensitiveWordBs.newInstance().wordTag(WordTags.file("dict_tag_test.txt")).init().findAll(text, WordResultHandlers.wordTags());
Assert.assertEquals("[WordTagsDto{word='五星红旗', tags=[政治, 国家]}, WordTagsDto{word='毛主席', tags=[政治, 伟人, 国家]}, WordTagsDto{word='天安门', tags=[政治, 国家, 地址]}]", wordList2.toString());

更多特性

后续的诸多特性,主要是针对各种针对各种情况的处理,尽可能的提升敏感词命中率。

这是一场漫长的攻防之战。

样式处理

忽略大小写

final String text = "fuCK the bad words.";String word = SensitiveWordHelper.findFirst(text);
Assert.assertEquals("fuCK", word);

忽略半角圆角

final String text = "fuck the bad words.";String word = SensitiveWordHelper.findFirst(text);
Assert.assertEquals("fuck", word);

忽略数字的写法

这里实现了数字常见形式的转换。

final String text = "这个是我的微信:9⓿二肆⁹₈③⑸⒋➃㈤㊄";List<String> wordList = SensitiveWordHelper.findAll(text);
Assert.assertEquals("[9⓿二肆⁹₈③⑸⒋➃㈤㊄]", wordList.toString());

忽略繁简体

final String text = "我爱我的祖国和五星紅旗。";List<String> wordList = SensitiveWordHelper.findAll(text);
Assert.assertEquals("[五星紅旗]", wordList.toString());

忽略英文的书写格式

final String text = "Ⓕⓤc⒦ the bad words";List<String> wordList = SensitiveWordHelper.findAll(text);
Assert.assertEquals("[Ⓕⓤc⒦]", wordList.toString());

忽略重复词

final String text = "ⒻⒻⒻfⓤuⓤ⒰cⓒ⒦ the bad words";List<String> wordList = SensitiveWordBs.newInstance().ignoreRepeat(true).init().findAll(text);
Assert.assertEquals("[ⒻⒻⒻfⓤuⓤ⒰cⓒ⒦]", wordList.toString());

更多检测策略

邮箱检测

final String text = "楼主好人,邮箱 sensitiveword@xx.com";List<String> wordList = SensitiveWordHelper.findAll(text);
Assert.assertEquals("[sensitiveword@xx.com]", wordList.toString());

连续数字检测

一般用于过滤手机号/QQ等广告信息。

V0.2.1 之后,支持通过 numCheckLen(长度) 自定义检测的长度。

final String text = "你懂得:12345678";// 默认检测 8 位
List<String> wordList = SensitiveWordBs.newInstance().init().findAll(text);
Assert.assertEquals("[12345678]", wordList.toString());// 指定数字的长度,避免误杀
List<String> wordList2 = SensitiveWordBs.newInstance().numCheckLen(9).init().findAll(text);
Assert.assertEquals("[]", wordList2.toString());

网址检测

用于过滤常见的网址信息。

final String text = "点击链接 www.baidu.com查看答案";List<String> wordList = SensitiveWordBs.newInstance().init().findAll(text);
Assert.assertEquals("[链接, www.baidu.com]", wordList.toString());Assert.assertEquals("点击** *************查看答案", SensitiveWordBs.newInstance().init().replace(text));

引导类特性配置

说明

上面的特性默认都是开启的,有时业务需要灵活定义相关的配置特性。

所以 v0.0.14 开放了属性配置。

配置方法

为了让使用更加优雅,统一使用 fluent-api 的方式定义。

用户可以使用 SensitiveWordBs 进行如下定义:

SensitiveWordBs wordBs = SensitiveWordBs.newInstance().ignoreCase(true).ignoreWidth(true).ignoreNumStyle(true).ignoreChineseStyle(true).ignoreEnglishStyle(true).ignoreRepeat(false).enableNumCheck(true).enableEmailCheck(true).enableUrlCheck(true).enableWordCheck(true).numCheckLen(8).wordTag(WordTags.none()).charIgnore(SensitiveWordCharIgnores.defaults()).init();final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。";
Assert.assertTrue(wordBs.contains(text));

配置说明

其中各项配置的说明如下:

序号方法说明默认值
1ignoreCase忽略大小写true
2ignoreWidth忽略半角圆角true
3ignoreNumStyle忽略数字的写法true
4ignoreChineseStyle忽略中文的书写格式true
5ignoreEnglishStyle忽略英文的书写格式true
6ignoreRepeat忽略重复词false
7enableNumCheck是否启用数字检测。true
8enableEmailCheck是有启用邮箱检测true
9enableUrlCheck是否启用链接检测true
10enableWordCheck是否启用敏感单词检测true
11numCheckLen数字检测,自定义指定长度。8
12wordTag词对应的标签none
13charIgnore忽略的字符none

忽略字符

说明

我们的敏感词一般都是比较连续的,比如【傻帽】

那就有大聪明发现,可以在中间加一些字符,比如【傻!@#$帽】跳过检测,但是骂人等攻击力不减。

那么,如何应对这些类似的场景呢?

我们可以指定特殊字符的跳过集合,忽略掉这些无意义的字符即可。

v0.11.0 开始支持

例子

其中 charIgnore 对应的字符策略,用户可以自行灵活定义。

final String text = "傻@冒,狗+东西";//默认因为有特殊字符分割,无法识别
List<String> wordList = SensitiveWordBs.newInstance().init().findAll(text);
Assert.assertEquals("[]", wordList.toString());// 指定忽略的字符策略,可自行实现。
List<String> wordList2 = SensitiveWordBs.newInstance().charIgnore(SensitiveWordCharIgnores.specialChars()).init().findAll(text);Assert.assertEquals("[傻@冒, 狗+东西]", wordList2.toString());

敏感词标签

说明

有时候我们希望对敏感词加一个分类标签:比如社情、暴/力等等。

这样后续可以按照标签等进行更多特性操作,比如只处理某一类的标签。

支持版本:v0.10.0

入门例子

接口

这里只是一个抽象的接口,用户可以自行定义实现。比如从数据库查询等。

public interface IWordTag {/*** 查询标签列表* @param word 脏词* @return 结果*/Set<String> getTag(String word);}

配置文件

我们可以自定义 dict 标签文件,通过 WordTags.file() 创建一个 WordTag 实现。

  • dict_tag_test.txt
五星红旗 政治,国家

格式如下:

敏感词 tag1,tag2

实现

具体的效果如下,在引导类设置一下即可。

默认的 wordTag 是空的。

String filePath = "dict_tag_test.txt";
IWordTag wordTag = WordTags.file(filePath);SensitiveWordBs sensitiveWordBs = SensitiveWordBs.newInstance().wordTag(wordTag).init();Assert.assertEquals("[政治, 国家]", sensitiveWordBs.tags("五星红旗").toString());;

后续会考虑引入一个内置的标签文件策略。

动态加载(用户自定义)

情景说明

有时候我们希望将敏感词的加载设计成动态的,比如控台修改,然后可以实时生效。

v0.0.13 支持了这种特性。

接口说明

为了实现这个特性,并且兼容以前的功能,我们定义了两个接口。

IWordDeny

接口如下,可以自定义自己的实现。

返回的列表,表示这个词是一个敏感词。

/*** 拒绝出现的数据-返回的内容被当做是敏感词* @author binbin.hou* @since 0.0.13*/
public interface IWordDeny {/*** 获取结果* @return 结果* @since 0.0.13*/List<String> deny();}

比如:

public class MyWordDeny implements IWordDeny {@Overridepublic List<String> deny() {return Arrays.asList("我的自定义敏感词");}}

IWordAllow

接口如下,可以自定义自己的实现。

返回的列表,表示这个词不是一个敏感词。

/*** 允许的内容-返回的内容不被当做敏感词* @author binbin.hou* @since 0.0.13*/
public interface IWordAllow {/*** 获取结果* @return 结果* @since 0.0.13*/List<String> allow();}

如:

public class MyWordAllow implements IWordAllow {@Overridepublic List<String> allow() {return Arrays.asList("五星红旗");}}

配置使用

接口自定义之后,当然需要指定才能生效。

为了让使用更加优雅,我们设计了引导类 SensitiveWordBs

可以通过 wordDeny() 指定敏感词,wordAllow() 指定非敏感词,通过 init() 初始化敏感词字典。

系统的默认配置

SensitiveWordBs wordBs = SensitiveWordBs.newInstance().wordDeny(WordDenys.system()).wordAllow(WordAllows.system()).init();final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。";
Assert.assertTrue(wordBs.contains(text));

备注:init() 对于敏感词 DFA 的构建是比较耗时的,一般建议在应用初始化的时候只初始化一次。而不是重复初始化!

指定自己的实现

我们可以测试一下自定义的实现,如下:

String text = "这是一个测试,我的自定义敏感词。";SensitiveWordBs wordBs = SensitiveWordBs.newInstance().wordDeny(new MyWordDeny()).wordAllow(new MyWordAllow()).init();Assert.assertEquals("[我的自定义敏感词]", wordBs.findAll(text).toString());

这里只有 我的自定义敏感词 是敏感词,而 测试 不是敏感词。

当然,这里是全部使用我们自定义的实现,一般建议使用系统的默认配置+自定义配置。

可以使用下面的方式。

同时配置多个

  • 多个敏感词

WordDenys.chains() 方法,将多个实现合并为同一个 IWordDeny。

  • 多个白名单

WordAllows.chains() 方法,将多个实现合并为同一个 IWordAllow。

例子:

String text = "这是一个测试。我的自定义敏感词。";IWordDeny wordDeny = WordDenys.chains(WordDenys.system(), new MyWordDeny());
IWordAllow wordAllow = WordAllows.chains(WordAllows.system(), new MyWordAllow());SensitiveWordBs wordBs = SensitiveWordBs.newInstance().wordDeny(wordDeny).wordAllow(wordAllow).init();Assert.assertEquals("[我的自定义敏感词]", wordBs.findAll(text).toString());

这里都是同时使用了系统默认配置,和自定义的配置。

注意:我们初始化了新的 wordBs,那么用新的 wordBs 去判断。而不是用以前的 SensitiveWordHelper 工具方法,工具方法配置是默认的!

spring 整合

背景

实际使用中,比如可以在页面配置修改,然后实时生效。

数据存储在数据库中,下面是一个伪代码的例子,可以参考 SpringSensitiveWordConfig.java

要求,版本 v0.0.15 及其以上。

自定义数据源

简化伪代码如下,数据的源头为数据库。

MyDdWordAllow 和 MyDdWordDeny 是基于数据库为源头的自定义实现类。

@Configuration
public class SpringSensitiveWordConfig {@Autowiredprivate MyDdWordAllow myDdWordAllow;@Autowiredprivate MyDdWordDeny myDdWordDeny;/*** 初始化引导类* @return 初始化引导类* @since 1.0.0*/@Beanpublic SensitiveWordBs sensitiveWordBs() {SensitiveWordBs sensitiveWordBs = SensitiveWordBs.newInstance().wordAllow(WordAllows.chains(WordAllows.system(), myDdWordAllow)).wordDeny(myDdWordDeny)// 各种其他配置.init();return sensitiveWordBs;}}

敏感词库的初始化较为耗时,建议程序启动时做一次 init 初始化。

动态变更

为了保证敏感词修改可以实时生效且保证接口的尽可能简化,此处没有新增 add/remove 的方法。

而是在调用 sensitiveWordBs.init() 的时候,根据 IWordDeny+IWordAllow 重新构建敏感词库。

因为初始化可能耗时较长(秒级别),所有优化为 init 未完成时不影响旧的词库功能,完成后以新的为准

@Component
public class SensitiveWordService {@Autowiredprivate SensitiveWordBs sensitiveWordBs;/*** 更新词库** 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的方法。* 如果需要生效,则调用这个方法。** 说明:重新初始化不影响旧的方法使用。初始化完成后,会以新的为准。*/public void refresh() {// 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的方法,然后调用这个方法。sensitiveWordBs.init();}}

如上,你可以在数据库词库发生变更时,需要词库生效,主动触发一次初始化 sensitiveWordBs.init();

其他使用保持不变,无需重启应用。

Benchmark

V0.6.0 以后,添加对应的 benchmark 测试。

BenchmarkTimesTest

环境

测试环境为普通的笔记本:

处理器	12th Gen Intel(R) Core(TM) i7-1260P   2.10 GHz
机带 RAM	16.0 GB (15.7 GB 可用)
系统类型	64 位操作系统, 基于 x64 的处理器

ps: 不同环境会有差异,但是比例基本稳定。

测试效果记录

测试数据:100+ 字符串,循环 10W 次。

序号场景耗时备注
1只做敏感词,无任何格式转换1470ms,约 7.2W QPS追求极致性能,可以这样配置
2只做敏感词,支持全部格式转换2744ms,约 3.7W QPS满足大部分场景

后期 road-map

  • 移除单个汉字的敏感词,在中国,要把词组当做一次词,降低误判率。

  • 支持单个的敏感词变化?

remove、add、edit?

  • 敏感词标签接口支持

  • 敏感词处理时标签支持

  • wordData 的内存占用对比 + 优化

  • 用户指定自定义的词组,同时允许指定词组的组合获取,更加灵活

FormatCombine/CheckCombine/AllowDenyCombine 组合策略,允许用户自定义。

  • word check 策略的优化,统一遍历+转换

  • 添加 ThreadLocal 等性能优化

拓展阅读

敏感词工具实现思路

DFA 算法讲解

敏感词库优化流程

java 如何实现开箱即用的敏感词控台服务?

v0.11.0-敏感词新特性及对应标签文件

wechat

NLP 开源矩阵

pinyin 汉字转拼音

pinyin2hanzi 拼音转汉字

segment 高性能中文分词

opencc4j 中文繁简体转换

nlp-hanzi-similar 汉字相似度

word-checker 拼写检测

sensitive-word 敏感词

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/415340.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浅谈智慧路灯安全智能供电方案设计

摘要: 智慧路灯&#xff0c;作为智慧城市、新基建、城市更新的主要组成部分&#xff0c;近些年在各大城市已得到很好的落地和 应用&#xff0c;但其与传统路灯相比集成大量异元异构电子设备&#xff0c;这些设备的供电电压、接口形式、权属单位各不相同&#xff0c; 如何设计一…

搭建知识付费小程序平台:如何避免被坑,选择最佳方案?

随着知识经济的兴起&#xff0c;知识付费已经成为一种趋势。越来越多的人开始将自己的知识和技能进行变现&#xff0c;而知识付费小程序平台则成为了一个重要的渠道。然而&#xff0c;市面上的知识付费小程序平台琳琅满目&#xff0c;其中不乏一些不良平台&#xff0c;让老实人…

空间计算时代催生新一波巨大算力市场需求

什么是空间计算&#xff1f; 空间计算是一种整合虚拟现实&#xff08;VR&#xff09;、增强现实&#xff08;AR&#xff09;、混合现实&#xff08;MR&#xff09;等技术的计算模式&#xff0c;旨在将数字信息与真实世界融合在一起。这种融合创造了一个全新的计算环境&#xff…

a-range-picker获取开始和结束时间

效果图 代码块 <a-range-picker change"onChangeRangeDate" format"YYYY-MM-DD"/>onChangeRangeDate(value,dateString){this.startDatedateString[0]this.endDatedateString[1]}

蓝桥杯回文日期判断

思想&#xff1a;对于回文数的判断方法&#xff0c;最快的就是取其中一半的字符串长度&#xff0c;为s&#xff0c;然后将其进行翻转为s’ &#xff0c;再把两者进行拼接即可保证是回文数&#xff0c;这样子就解决了枚举所有回文数的问题。 注意点&#xff1a; 要求必须是有效…

【Vue3】2-13 : 章节总结

本书目录&#xff1a;点击进入 一、总结内容 二、习题 2.1 【选择题】以下Vue指令中&#xff0c;哪些指令具备简写方式&#xff1f; 2.2 【编程题】以下Vue指令中&#xff0c;哪些指令具备简写方式&#xff1f; &#xff1e; 效果 &#xff1e; 代码 一、总结内容 了解核…

stable-diffusion-webui 安装与运行

大家好&#xff0c;我是水滴~~ 本文主要介绍在各种显卡环境中&#xff0c;如何安装和运行 stable-diffusion-webui&#xff0c;包括&#xff1a;在 NVIDIA 显卡环境上安装、在 AMD 显卡环境上安装、在集显或小于 4G 显卡环境上安装。 文章目录 在 NVIDIA 显卡环境上安装下载配…

python如何包含其他路径的模块

python 包含其他路径的模块&#xff1a; 例如目录结构: dir1 |__ init.py |__ module1.py dir2 |__ main.py main.py from dir1 import module1首先需要在 dir1 添加 init.py 文件&#xff0c;该文件可以是空文件。 其次需要将dir1 的父目录添加到python 解释器的&#xf…

开发实践5_project

要求&#xff1a; &#xff08;对作业要求的"Student"稍作了变换&#xff0c;表单名称为“Index”。&#xff09;获得后台 Index 数据&#xff0c;作展示&#xff0c;要求使用分页器&#xff0c;包含上一页、下一页、当前页/总页。 结果&#xff1a; ① preparatio…

Qt —— 编译Qt5版本QFTP库,并实现连接服务、获取列表、上传、下载、删除文件等操作(附源码、附基于Qt5编译好的QFTP库)

示例效果1 示例效果2 介绍 QFTP是Qt4的库,Qt5改用了QNetworkAccessManager来代替。但是Qt5提供的QNetworkAccessManager仅支持FTP的上传和下载,所以只能将QFTP库编译为Qt5的库来进行调用。 QFTP在Github的下载地址:https://github.com/qt/qtftp 客户端源码生成的release结果…

详细介绍IP 地址、网络号和主机号、ABC三类、ip地址可分配问题、子网掩码、子网划分

1、 IP 地址: 网络之间互连的协议&#xff0c;是由4个字节(32位二进制)组成的逻辑上的地址。 将32位二进制进行分组&#xff0c;分成4组&#xff0c;每组8位(1个字节)。【ip地址通常使用十进制表示】ip地址分成四组之后&#xff0c;在逻辑上&#xff0c;分成网络号和主机号 2…

FairGuard游戏安全2023年度报告

导 读&#xff1a;2023年&#xff0c;游戏行业摆脱了疫情带来诸多负面影响&#xff0c;国内游戏市场收入与用户规模双双实现突破&#xff0c;迎来了历史新高点。但游戏黑灰产规模也在迅速扩大&#xff0c;不少游戏饱受其侵扰&#xff0c;游戏厂商愈发重视游戏安全问题。 为帮助…