Sensitive-Word es una herramienta de palabra sensible de alto rendimiento basada en el algoritmo DFA.
Experiencia en línea
Si tiene algunas enfermedades difíciles y complicadas, puede unirse: Technical Exchange Group
Sensitive-Word-Admin es la aplicación de consola correspondiente. Las funciones se encuentran actualmente en las primeras etapas de desarrollo y la versión MVP está disponible.
Hola a todos, soy Lao Ma.
Siempre he querido implementar una herramienta de palabra confidencial simple y fácil de usar, por lo que implementé esta herramienta de código abierto.
Basado en la implementación del algoritmo DFA, el contenido del tesauro sensible actualmente incluye 6W+ (archivo fuente 18W+, después de una eliminación).
En la etapa posterior, el tesauro sensible se optimizará y complementará continuamente, y el rendimiento del algoritmo mejorará aún más.
V0.24.0 ha comenzado a apoyar la clasificación y el refinamiento de palabras sensibles, pero la carga de trabajo es relativamente grande, por lo que inevitablemente hay omisiones.
¡Bienvenido a mejoras de relaciones públicas, solicitudes de GitHub o únase al grupo de intercambio técnico para comunicarse y presumir!
6W+ Tesauro, y optimización y actualización continua
Basado en la implementación fluida-API, uso elegante y conciso
Basado en el algoritmo DFA, el rendimiento es 7W+ QPS, la aplicación es insensible
Apoyar operaciones comunes como el juicio, el retorno y la desensibilización de palabras sensibles
Admite la conversión de formato común
Intercambio de medio ancho de ancho completo, intercambio de casos en inglés, formas comunes de números, chino tradicional y chino simplificado, formas comunes de intercambio inglés, ignorando palabras duplicadas, etc.
Admite detección de palabras confidencial, detección de correo electrónico, detección digital, detección de sitios web, IPv4, etc.
Admite políticas de reemplazo personalizadas
Admite palabras confidenciales y listas blancas definidas por el usuario
Admite actualizaciones de datos dinámicos (personalización del usuario), efectiva en tiempo real
Admite la interfaz de etiqueta + implementación de clasificación incorporada para palabras confidenciales
Soporte omitir algunos caracteres especiales para hacer que la coincidencia sea más flexible
Admite suma/modificación de una sola modificación de listas en blanco y negro, sin inicialización completa
Change_log.md
A veces hay una consola para palabras sensibles, lo que la hace más flexible y conveniente de configurar.
¿Cómo implementar el servicio de consola de palabras confidencial de la caja?
Se han resuelto una gran cantidad de archivos de etiqueta de palabras confidenciales, lo que puede hacer que nuestras palabras sensibles sean más convenientes.
Estos dos materiales se pueden leer en el artículo a continuación:
V0.11.0 nuevas características de palabras confidenciales y archivos de etiqueta correspondientes
Actualmente, V0.24.0 tiene etiquetas de palabras incorporadas, y se recomienda actualizar a la última versión si es necesario.
El código abierto no es fácil. Si este proyecto es útil para usted, puede invitar a Lao Ma a tomar una taza de té de leche.

JDK1.8+
Maven 3.x+
< dependency >
< groupId >com.github.houbb</ groupId >
< artifactId >sensitive-word</ artifactId >
< version >0.24.0</ version >
</ dependency > SensitiveWordHelper es una clase de herramientas para palabras sensibles. Los métodos centrales son los siguientes:
Nota: SensitiveWordHelper proporciona configuraciones predeterminadas. Si desea realizar configuraciones personalizadas flexibles, consulte la configuración de la función de clase de arranque
| método | parámetro | Valor de retorno | ilustrar |
|---|---|---|---|
| contiene (cadena) | La cadena a verificar | Valor booleano | Verifique que la cadena contenga palabras sensibles |
| reemplazar (cadena, isensitivewordReplace) | Reemplazar palabras sensibles con una estrategia de reemplazo especificada | Cadena | Devuelve la cadena desensibilizada |
| reemplazar (cadena, char) | Use el char especificado para reemplazar la palabra sensible | Cadena | Devuelve la cadena desensibilizada |
| reemplazar (cadena) | Usar * para reemplazar palabras sensibles | Cadena | Devuelve la cadena desensibilizada |
| findall (cadena) | La cadena a verificar | Lista de cuerdas | Devuelve todas las palabras sensibles en la cadena |
| findfirst (cadena) | La cadena a verificar | Cadena | Devuelve la primera palabra sensible en la cadena |
| findall (string, iwordresulthandler) | IwordresulThandler RESULTS CLASE | Lista de cuerdas | Devuelve todas las palabras sensibles en la cadena |
| findfirst (string, iwordresulthandler) | IwordresulThandler RESULTS CLASE | Cadena | Devuelve la primera palabra sensible en la cadena |
| Etiquetas (cadena) | Obtenga etiquetas para palabras sensibles | Cadena de palabras sensible | Vuelva a la lista de etiquetas para palabras sensibles |
final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。" ;
Assert . assertTrue ( SensitiveWordHelper . contains ( text )); final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。" ;
String word = SensitiveWordHelper . findFirst ( text );
Assert . assertEquals ( "五星红旗" , word );SensitiveWordHelper.FindFirst (Text) es equivalente a:
String word = SensitiveWordHelper . findFirst ( text , WordResultHandlers . word ()); final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。" ;
List < String > wordList = SensitiveWordHelper . findAll ( text );
Assert . assertEquals ( "[五星红旗, 毛主席, 天安门]" , wordList . toString ());Devuelve todo el uso de palabras confidenciales es similar a SensitiveWordHelper.findFirst (), y también admite especificar las clases de procesamiento de resultados.
SensitiveWordHelper.findall (texto) es equivalente a:
List < String > wordList = SensitiveWordHelper . findAll ( text , WordResultHandlers . word ());WORDRESULTHANDLERS.RAW () puede retener la información de subíndice y la información de categoría correspondiente:
final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。" ;
// 默认敏感词标签为空
List < WordTagsDto > wordList1 = SensitiveWordHelper . findAll ( text , WordResultHandlers . wordTags ());
Assert . assertEquals ( "[WordTagsDto{word='五星红旗', tags=[]}, WordTagsDto{word='毛主席', tags=[]}, WordTagsDto{word='天安门', tags=[]}]" , wordList1 . 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 admite esta función.
Descripción de la escena: a veces queremos diferentes palabras sensibles para tener diferentes resultados de reemplazo. Por ejemplo, [el juego] se reemplaza con [E-Sports], y [desempleo] se reemplaza por [empleo flexible].
Es cierto que está bien usar el reemplazo regular de cuerdas por adelantado, pero el rendimiento es promedio.
Ejemplo de uso:
/**
* 自定替换策略
* @since 0.2.0
*/
@ Test
public void defineReplaceTest () {
final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。" ;
ISensitiveWordReplace replace = new MySensitiveWordReplace ();
String result = SensitiveWordHelper . replace ( text , replace );
Assert . assertEquals ( "国家旗帜迎风飘扬,教员的画像屹立在***前。" , result );
} Entre ellos, MySensitiveWordReplace está nuestra política de reemplazo personalizada, que se implementa de la siguiente manera:
public class MyWordReplace implements IWordReplace {
@ Override
public 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 ( '*' );
}
}
}
} Hacemos mapeo fijo para algunas de las palabras, y las otras se convierten en * por defecto.
IWordresulThandler puede procesar los resultados de palabras confidenciales y permitir a los usuarios personalizarlas.
Consulte la clase de herramientas WordResultHandlers para la implementación incorporada:
Solo se conservan palabras sensibles.
Retener información relacionada con palabras confidenciales, incluidos los subíndices iniciales y finales de palabras confidenciales.
Al mismo tiempo, se conservan las palabras y la información de etiqueta de palabras correspondiente.
Ver SensitiveWordHelpertest para todos los casos de prueba
1) Ejemplos básicos
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 ()); Especificamos la información de la etiqueta de la palabra correspondiente en el archivo 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 ());Las características posteriores están dirigidas principalmente a diversas situaciones de procesamiento, para mejorar la tasa de aciertos de las palabras sensibles tanto como sea posible.
Esta es una larga batalla de ofensiva y defensa.
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 );Aquí, se implementa la conversión de formas comunes de digital.
final String text = "这个是我的微信:9⓿二肆⁹₈③⑸⒋➃㈤㊄" ;
List < String > wordList = SensitiveWordBs . newInstance (). enableNumCheck ( true ). init (). 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 ());La información personal como la dirección de correo electrónico no está habilitada de forma predeterminada.
final String text = "楼主好人,邮箱 [email protected]" ;
List < String > wordList = SensitiveWordBs . newInstance (). enableEmailCheck ( true ). init (). findAll ( text );
Assert . assertEquals ( "[[email protected]]" , wordList . toString ());Generalmente se usa para filtrar información publicitaria como el número de teléfono móvil/QQ, y no está habilitado de forma predeterminada.
Después de V0.2.1, se admiten las longitudes detectadas por numCheckLen(长度) .
final String text = "你懂得:12345678" ;
// 默认检测 8 位
List < String > wordList = SensitiveWordBs . newInstance ()
. enableNumCheck ( true )
. init (). findAll ( text );
Assert . assertEquals ( "[12345678]" , wordList . toString ());
// 指定数字的长度,避免误杀
List < String > wordList2 = SensitiveWordBs . newInstance ()
. enableNumCheck ( true )
. numCheckLen ( 9 )
. init ()
. findAll ( text );
Assert . assertEquals ( "[]" , wordList2 . toString ());Se utiliza para filtrar información de URL común, no habilitada de forma predeterminada.
V0.18.0 optimiza la detección de URL, que es más estricto y reduce la tasa de juicio erróneo
final String text = "点击链接 https://www.baidu.com 查看答案" ;
final SensitiveWordBs sensitiveWordBs = SensitiveWordBs . newInstance (). enableUrlCheck ( true ). init ();
List < String > wordList = sensitiveWordBs . findAll ( text );
Assert . assertEquals ( "[https://www.baidu.com]" , wordList . toString ());
Assert . assertEquals ( "点击链接 ********************* 查看答案" , sensitiveWordBs . replace ( text ));V0.17.0 Soporte
Evite los usuarios evitando la detección de URL a través de IP, etc., que no está habilitado de forma predeterminada.
final String text = "个人网站,如果网址打不开可以访问 127.0.0.1。" ;
final SensitiveWordBs sensitiveWordBs = SensitiveWordBs . newInstance (). enableIpv4Check ( true ). init ();
List < String > wordList = sensitiveWordBs . findAll ( text );
Assert . assertEquals ( "[127.0.0.1]" , wordList . toString ());Las características anteriores están habilitadas de forma predeterminada, y a veces el negocio necesita definir de manera flexible las características de configuración relacionadas.
Entonces V0.0.14 abre la configuración de la propiedad.
Para hacer uso más elegante, la definición es utilizada uniformemente por Fluent-API.
Los usuarios pueden usar SensitiveWordBs para definirlo de la siguiente manera:
Nota: Después de la configuración, use nuestro objeto SensitiveWordBs recientemente definido en lugar del método de herramienta anterior. La configuración del método de la herramienta es todo predeterminado.
SensitiveWordBs wordBs = SensitiveWordBs . newInstance ()
. ignoreCase ( true )
. ignoreWidth ( true )
. ignoreNumStyle ( true )
. ignoreChineseStyle ( true )
. ignoreEnglishStyle ( true )
. ignoreRepeat ( false )
. enableNumCheck ( false )
. enableEmailCheck ( false )
. enableUrlCheck ( false )
. enableIpv4Check ( false )
. enableWordCheck ( true )
. numCheckLen ( 8 )
. wordTag ( WordTags . none ())
. charIgnore ( SensitiveWordCharIgnores . defaults ())
. wordResultCondition ( WordResultConditions . alwaysTrue ())
. init ();
final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。" ;
Assert . assertTrue ( wordBs . contains ( text ));La descripción de cada configuración es la siguiente:
| Número de serie | método | ilustrar | valor predeterminado |
|---|---|---|---|
| 1 | ignorarse | Ignorar el caso | verdadero |
| 2 | ñan ignorante | Ignorar las esquinas redondeadas de la mitad de la esquina | verdadero |
| 3 | estilo ignorenum | Ignorar la escritura de números | verdadero |
| 4 | Ignorar chinesestyle | Ignorar el formato de escritura chino | verdadero |
| 5 | Estilo IgnorenEnglish | Ignorar el formato de escritura en inglés | verdadero |
| 6 | encapritarse | Ignorar palabras duplicadas | FALSO |
| 7 | habilitar | Si habilita la detección digital. | FALSO |
| 8 | habilitarEmailcheck | La detección de correo electrónico está habilitada | FALSO |
| 9 | HabilitarCheck | Si habilitar la detección de enlaces | FALSO |
| 10 | HabilitarIPV4CHECK | Si habilita la detección de IPv4 | FALSO |
| 11 | HabilitarwordCheck | Si habilita la detección de palabras sensible | verdadero |
| 12 | Numchecklen | Detección digital, personalice la longitud especificada. | 8 |
| 13 | Wordtag | Las etiquetas de palabras correspondientes | ninguno |
| 14 | charignore | Personajes ignorados | ninguno |
| 15 | WordResultcondition | Procesamiento adicional para palabras confidenciales coincidentes, como limitar la necesidad de coincidencias completas de palabras en inglés | Siempre es cierto |
V0.16.1 es compatible. A veces necesitamos liberar la memoria, lo que puede ser el siguiente:
Sobre problemas de reciclaje de memoria
SensitiveWordBs wordBs = SensitiveWordBs . newInstance ()
. init ();
// 后续因为一些原因移除了对应信息,希望释放内存。
wordBs . destroy ();Escenario de uso: después de la inicialización, queremos agregar/eliminar una sola palabra en lugar de reiniciarla por completo. Esta característica está preparada para esto.
Versión compatible: V0.19.0
addWord(word) agrega palabras confidenciales, admitiendo palabras/colecciones individuales
removeWord(word) elimina palabras confidenciales, admite palabras/colecciones únicas
final String text = "测试一下新增敏感词,验证一下删除和新增对不对" ;
SensitiveWordBs sensitiveWordBs =
SensitiveWordBs . newInstance ()
. wordAllow ( WordAllows . empty ())
. wordDeny ( WordDenys . empty ())
. init ();
// 当前
Assert . assertEquals ( "[]" , sensitiveWordBs . findAll ( text ). toString ());
// 新增单个
sensitiveWordBs . addWord ( "测试" );
sensitiveWordBs . addWord ( "新增" );
Assert . assertEquals ( "[测试, 新增, 新增]" , sensitiveWordBs . findAll ( text ). toString ());
// 删除单个
sensitiveWordBs . removeWord ( "新增" );
Assert . assertEquals ( "[测试]" , sensitiveWordBs . findAll ( text ). toString ());
sensitiveWordBs . removeWord ( "测试" );
Assert . assertEquals ( "[]" , sensitiveWordBs . findAll ( text ). toString ());
// 新增集合
sensitiveWordBs . addWord ( Arrays . asList ( "新增" , "测试" ));
Assert . assertEquals ( "[测试, 新增, 新增]" , sensitiveWordBs . findAll ( text ). toString ());
// 删除集合
sensitiveWordBs . removeWord ( Arrays . asList ( "新增" , "测试" ));
Assert . assertEquals ( "[]" , sensitiveWordBs . findAll ( text ). toString ());
// 新增数组
sensitiveWordBs . addWord ( "新增" , "测试" );
Assert . assertEquals ( "[测试, 新增, 新增]" , sensitiveWordBs . findAll ( text ). toString ());
// 删除集合
sensitiveWordBs . removeWord ( "新增" , "测试" );
Assert . assertEquals ( "[]" , sensitiveWordBs . findAll ( text ). toString ());Escenario de uso: después de la inicialización, queremos agregar/eliminar una sola palabra en lugar de reiniciarla por completo. Esta característica está preparada para esto.
Versión compatible: V0.21.0
addWordAllow(word) agrega una nueva lista blanca, admite palabras/colecciones individuales
removeWordAllow(word) Elimina las basales, admite palabras/colecciones individuales
final String text = "测试一下新增敏感词白名单,验证一下删除和新增对不对" ;
SensitiveWordBs sensitiveWordBs =
SensitiveWordBs . newInstance ()
. wordAllow ( WordAllows . empty ())
. wordDeny ( new IWordDeny () {
@ Override
public List < String > deny () {
return Arrays . asList ( "测试" , "新增" );
}
})
. init ();
// 当前
Assert . assertEquals ( "[测试, 新增, 新增]" , sensitiveWordBs . findAll ( text ). toString ());
// 新增单个
sensitiveWordBs . addWordAllow ( "测试" );
sensitiveWordBs . addWordAllow ( "新增" );
Assert . assertEquals ( "[]" , sensitiveWordBs . findAll ( text ). toString ());
// 删除单个
sensitiveWordBs . removeWordAllow ( "测试" );
Assert . assertEquals ( "[测试]" , sensitiveWordBs . findAll ( text ). toString ());
sensitiveWordBs . removeWordAllow ( "新增" );
Assert . assertEquals ( "[测试, 新增, 新增]" , sensitiveWordBs . findAll ( text ). toString ());
// 新增集合
sensitiveWordBs . addWordAllow ( Arrays . asList ( "新增" , "测试" ));
Assert . assertEquals ( "[]" , sensitiveWordBs . findAll ( text ). toString ());
// 删除集合
sensitiveWordBs . removeWordAllow ( Arrays . asList ( "新增" , "测试" ));
Assert . assertEquals ( "[测试, 新增, 新增]" , sensitiveWordBs . findAll ( text ). toString ());
// 新增数组
sensitiveWordBs . addWordAllow ( "新增" , "测试" );
Assert . assertEquals ( "[]" , sensitiveWordBs . findAll ( text ). toString ());
// 删除集合
sensitiveWordBs . removeWordAllow ( "新增" , "测试" );
Assert . assertEquals ( "[测试, 新增, 新增]" , sensitiveWordBs . findAll ( text ). toString ());Este método está abandonado . Se recomienda utilizar el método de adición incremental anterior para evitar la carga completa. Para la compatibilidad, este método permanece.
Cómo usar: cuando llame sensitiveWordBs.init() , reconstruya el vocabulario sensible basado en iWorddeny+iWordallow. Debido a que la inicialización puede llevar mucho tiempo (segundo nivel), todas las optimizaciones a Init no afectarán la antigua función de vocabulario cuando no se complete, y la nueva prevalecerá después de la finalización .
@ Component
public class SensitiveWordService {
@ Autowired
private SensitiveWordBs sensitiveWordBs ;
/**
* 更新词库
*
* 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的方法。
* 如果需要生效,则调用这个方法。
*
* 说明:重新初始化不影响旧的方法使用。初始化完成后,会以新的为准。
*/
public void refresh () {
// 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的方法,然后调用这个方法。
sensitiveWordBs . init ();
}
} Como se mencionó anteriormente, puede activar activamente la inicialización de sensitiveWordBs.init(); Cuando la base de datos cambia el léxico y la base de datos debe surtir efecto.
Otros usos siguen siendo los mismos sin reiniciar la aplicación.
Versión compatible: V0.13.0
A veces es posible que deseemos limitar aún más las palabras sensibles a juego, por ejemplo, aunque definimos [av] como una palabra sensible, no queremos que [tengamos] ser emparejados.
Puede personalizar la interfaz WORDRESULTCONDITION e implementar sus propias políticas.
La política incorporada en WordResultConditions#alwaysTrue() siempre es cierta, mientras que WordResultConditions#englishWordMatch() requiere que el idioma inglés coincida con la palabra completa.
La clase de herramientas de WordDresultConditions puede obtener políticas coincidentes
| lograr | ilustrar | Versión compatible |
|---|---|---|
| siempre True | Siempre es cierto | |
| Ingléswordmatch | Palabra en inglés Palabra completa coincidir | V0.13.0 |
| EnglishWordNummatch | Palabra/número de inglés Coincidencia de palabra completa | V0.20.0 |
| Wordtags | Aquellos que satisfacen etiquetas específicas, como solo centrarse en las etiquetas [publicidad] | V0.23.0 |
| cadenas (iWordResultCondition ... Condiciones) | Admite especificar múltiples condiciones y satisfacerlas al mismo tiempo | V0.23.0 |
Situación predeterminada original:
final String text = "I have a nice day。" ;
List < String > wordList = SensitiveWordBs . newInstance ()
. wordDeny ( new IWordDeny () {
@ Override
public List < String > deny () {
return Collections . singletonList ( "av" );
}
})
. wordResultCondition ( WordResultConditions . alwaysTrue ())
. init ()
. findAll ( text );
Assert . assertEquals ( "[av]" , wordList . toString ());Podemos especificar que el inglés debe coincidir con la palabra completa.
final String text = "I have a nice day。" ;
List < String > wordList = SensitiveWordBs . newInstance ()
. wordDeny ( new IWordDeny () {
@ Override
public List < String > deny () {
return Collections . singletonList ( "av" );
}
})
. wordResultCondition ( WordResultConditions . englishWordMatch ())
. init ()
. findAll ( text );
Assert . assertEquals ( "[]" , wordList . toString ());Por supuesto, se pueden implementar estrategias más complejas según sea necesario.
Versión compatible: v0.23.0
Solo podemos devolver palabras sensibles afiliadas a una determinada etiqueta.
Hemos especificado dos palabras sensibles: producto, AV
MyWordTag es una implementación de etiqueta de palabra sensible que definimos:
/**
* 自定义单词标签
* @since 0.23.0
*/
public class MyWordTag extends AbstractWordTag {
private static Map < String , Set < String >> dataMap ;
static {
dataMap = new HashMap <>();
dataMap . put ( "商品" , buildSet ( "广告" , "中文" ));
dataMap . put ( "AV" , buildSet ( "色情" , "单词" , "英文" ));
}
private static Set < String > buildSet ( String ... tags ) {
Set < String > set = new HashSet <>();
for ( String tag : tags ) {
set . add ( tag );
}
return set ;
}
@ Override
protected Set < String > doGetTag ( String word ) {
return dataMap . get ( word );
}
}Por ejemplo, simulamos dos clases de implementación diferentes, cada una centrada en una etiqueta de palabra diferente.
// 只关心SE情
SensitiveWordBs sensitiveWordBsYellow = SensitiveWordBs . newInstance ()
. wordDeny ( new IWordDeny () {
@ Override
public List < String > deny () {
return Arrays . asList ( "商品" , "AV" );
}
})
. wordAllow ( WordAllows . empty ())
. wordTag ( new MyWordTag ())
. wordResultCondition ( WordResultConditions . wordTags ( Arrays . asList ( "色情" )))
. init ();
// 只关心广告
SensitiveWordBs sensitiveWordBsAd = SensitiveWordBs . newInstance ()
. wordDeny ( new IWordDeny () {
@ Override
public List < String > deny () {
return Arrays . asList ( "商品" , "AV" );
}
})
. wordAllow ( WordAllows . empty ())
. wordTag ( new MyWordTag ())
. wordResultCondition ( WordResultConditions . wordTags ( Arrays . asList ( "广告" )))
. init ();
final String text = "这些 AV 商品什么价格?" ;
Assert . assertEquals ( "[AV]" , sensitiveWordBsYellow . findAll ( text ). toString ());
Assert . assertEquals ( "[商品]" , sensitiveWordBsAd . findAll ( text ). toString ());Nuestras palabras sensibles son generalmente más continuas, como [sombrero tonto]
Luego hay un descubrimiento inteligente de que puedes agregar algunos personajes en el medio, como [¡tonto! @#$ Hat] para omitir la detección, pero el poder de ataque de la juramentación no se reduce.
Entonces, ¿cómo lidiar con estos escenarios similares?
Podemos especificar los conjuntos de caracteres especiales e ignorar estos personajes sin sentido.
V0.11.0 inicia soporte
La estrategia de caracteres correspondiente a Charignore puede ser definida de manera flexible por los usuarios.
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 ());A veces queremos agregar una etiqueta clasificada a palabras sensibles: como situación social, violencia, etc.
De esta manera, se pueden realizar más características de acuerdo con las etiquetas, etc., como solo procesar un cierto tipo de etiqueta.
Versión compatible: V0.10.0
Versión de soporte de características principales: V0.24.0
Esta es solo una interfaz abstracta, y los usuarios pueden definir la implementación por sí mismos. Por ejemplo, de la consulta de la base de datos, lectura de archivos, llamadas de API, etc.
public interface IWordTag {
/**
* 查询标签列表
* @param word 脏词
* @return 结果
*/
Set < String > getTag ( String word );
} Para facilitar el uso en la mayoría de las situaciones, algunas estrategias de escena se implementan en WordTags
| Método de implementación | ilustrar | Observación |
|---|---|---|
| ninguno() | Implementación vacía | V0.10.0 Soporte |
| Archivo (cadena FilePath) | Especificar la ruta del archivo | V0.10.0 Soporte |
| archivo (string filepath, string wordsplit, string tagsplit) | Especificar rutas de archivo, así como separadores de palabras y separadores de etiquetas | V0.10.0 Soporte |
| mapa (mapa final <string, set> wordTagMap) | Inicialización según el mapa | V0.24.0 Soporte |
| líneas (líneas de recolección) | Lista de cuerdas | V0.24.0 Soporte |
| Líneas (líneas de recolección, String Wordsplit, String Tagspli) | Lista de cuerdas, así como separadores de palabras y separadores de etiquetas | V0.24.0 Soporte |
| sistema() | Implementación incorporada de los archivos del sistema, integrando la clasificación de la red | V0.24.0 Soporte |
| predeterminado () | La política predeterminada es actualmente sistema | V0.24.0 Soporte |
| cadenas (iwordtag ... otros) | Método de cadena, admite la integración del usuario para implementar múltiples políticas | V0.24.0 Soporte |
El formato de etiquetas de palabras confidenciales que predeterminamos las siguientes敏感词tag1,tag2 , lo que significa que las etiquetas de敏感词son TAG1 y TAG2.
Por ejemplo
五星红旗 政治,国家
Esto también se recomienda para todos los contenidos de línea de archivo y contenido de cadena especificado. Si no está satisfecho, simplemente impleméntelo de manera personalizada.
Comenzando con V0.24.0, la etiqueta de palabra predeterminada es WordTags.system() .
Nota: Actualmente, las estadísticas de datos son de Internet, y hay muchas omisiones. Todos también son bienvenidos para corregir el problema y continuar mejorando ...
SensitiveWordBs sensitiveWordBs = SensitiveWordBs . newInstance ()
. wordTag ( WordTags . system ())
. init ();
Set < String > tagSet = sensitiveWordBs . tags ( "博彩" );
Assert . assertEquals ( "[3]" , tagSet . toString ());Aquí, para optimizar el tamaño de la compresión, las categorías correspondientes están representadas por números.
La lista de números de significado es la siguiente:
0 政治
1 毒品
2 色情
3 赌博
4 违法
Aquí tomamos el archivo como ejemplo para demostrar cómo usarlo.
final String path = "~ \ test \ resources \ dict_tag_test.txt" ;
// 演示默认方法
IWordTag wordTag = WordTags . file ( path );
SensitiveWordBs sensitiveWordBs = SensitiveWordBs . newInstance ()
. wordTag ( wordTag )
. init ();
Set < String > tagSet = sensitiveWordBs . tags ( "零售" );
Assert . assertEquals ( "[广告, 网络]" , tagSet . toString ());
// 演示指定分隔符
IWordTag wordTag2 = WordTags . file ( path , " " , "," );
SensitiveWordBs sensitiveWordBs2 = SensitiveWordBs . newInstance ()
. wordTag ( wordTag2 )
. init ();
Set < String > tagSet2 = sensitiveWordBs2 . tags ( "零售" );
Assert . assertEquals ( "[广告, 网络]" , tagSet2 . toString ()); Donde dict_tag_test.txt Nuestro contenido personalizado es el siguiente:
零售 广告,网络
Cuando obtenemos palabras confidenciales, podemos establecer la estrategia de procesamiento de resultados correspondiente para obtener la información de etiqueta de palabra confidencial correspondiente
// 自定义测试标签类
IWordTag wordTag = WordTags . lines ( Arrays . asList ( "天安门 政治,国家,地址" ));
// 指定初始化
SensitiveWordBs sensitiveWordBs = SensitiveWordBs . newInstance ()
. wordTag ( wordTag )
. init ()
;
List < WordTagsDto > wordTagsDtoList1 = sensitiveWordBs . findAll ( "天安门" , WordResultHandlers . wordTags ());
Assert . assertEquals ( "[WordTagsDto{word='天安门', tags=[政治, 国家, 地址]}]" , wordTagsDtoList1 . toString ()); Personalizamos las etiquetas para las palabras clave天安门, y luego especificamos que la estrategia de procesamiento de resultados de Findall es WordResultHandlers.wordTags() , y podemos obtener la lista de etiquetas correspondiente al obtener palabras sensibles.
A veces queremos diseñar la carga de palabras sensibles en dinámica, como la modificación de la consola, que luego puede entrar en vigencia en tiempo real.
V0.0.13 admite esta característica.
Para implementar esta característica y ser compatible con funciones anteriores, definimos dos interfaces.
La interfaz es la siguiente, puede personalizar su propia implementación.
La lista devuelta significa que la palabra es una palabra sensible.
/**
* 拒绝出现的数据-返回的内容被当做是敏感词
* @author binbin.hou
* @since 0.0.13
*/
public interface IWordDeny {
/**
* 获取结果
* @return 结果
* @since 0.0.13
*/
List < String > deny ();
}Por ejemplo:
public class MyWordDeny implements IWordDeny {
@ Override
public List < String > deny () {
return Arrays . asList ( "我的自定义敏感词" );
}
}La interfaz es la siguiente, puede personalizar su propia implementación.
La lista devuelta significa que la palabra no es una palabra sensible.
/**
* 允许的内容-返回的内容不被当做敏感词
* @author binbin.hou
* @since 0.0.13
*/
public interface IWordAllow {
/**
* 获取结果
* @return 结果
* @since 0.0.13
*/
List < String > allow ();
}como:
public class MyWordAllow implements IWordAllow {
@ Override
public List < String > allow () {
return Arrays . asList ( "五星红旗" );
}
}Después de que la interfaz esté personalizada, por supuesto, debe especificarse para entrar en vigencia.
Para hacer usar más elegante, diseñamos la clase de arranque SensitiveWordBs .
Puede especificar palabras confidenciales a través de WordDeny (), WordAlow () especifica palabras no sensibles e inicializar el diccionario de palabras sensibles a través de init ().
SensitiveWordBs wordBs = SensitiveWordBs . newInstance ()
. wordDeny ( WordDenys . defaults ())
. wordAllow ( WordAllows . defaults ())
. init ();
final String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。" ;
Assert . assertTrue ( wordBs . contains ( text ));Nota: Init () requiere mucho tiempo para construir la palabra sensible DFA. Generalmente se recomienda que se inicialice solo una vez al aplicar la inicialización. ¡En lugar de repetir la inicialización!
Podemos probar la implementación personalizada, como sigue:
String text = "这是一个测试,我的自定义敏感词。" ;
SensitiveWordBs wordBs = SensitiveWordBs . newInstance ()
. wordDeny ( new MyWordDeny ())
. wordAllow ( new MyWordAllow ())
. init ();
Assert . assertEquals ( "[我的自定义敏感词]" , wordBs . findAll ( text ). toString ()); Aquí está el único en el que我的自定义敏感词son palabras sensibles, y测试no son palabras sensibles.
Por supuesto, aquí están todas nuestras implementaciones personalizadas. Generalmente se recomienda utilizar la configuración predeterminada + configuración personalizada del sistema.
Se puede usar el siguiente método.
WordDenys.chains() Método fusiona múltiples implementaciones en el mismo iWorddeny.
WordAllows.chains() El método fusiona múltiples implementaciones en el mismo iWordallow.
ejemplo:
String text = "这是一个测试。我的自定义敏感词。" ;
IWordDeny wordDeny = WordDenys . chains ( WordDenys . defaults (), new MyWordDeny ());
IWordAllow wordAllow = WordAllows . chains ( WordAllows . defaults (), new MyWordAllow ());
SensitiveWordBs wordBs = SensitiveWordBs . newInstance ()
. wordDeny ( wordDeny )
. wordAllow ( wordAllow )
. init ();
Assert . assertEquals ( "[我的自定义敏感词]" , wordBs . findAll ( text ). toString ());Todos usan la configuración predeterminada del sistema y la configuración personalizada al mismo tiempo.
Nota: Inicializamos nuevos WordB, así que use nuevos WordB para juzgar. En lugar de usar el método de herramienta SensitiveWordHelper anterior anterior, ¡la configuración del método de herramienta es el valor predeterminado!
En uso real, por ejemplo, puede modificar la configuración de la página y luego entrar en efecto en tiempo real.
Los datos se almacenan en la base de datos. El siguiente es un ejemplo de pseudo-código. Puede consultar SpringSensitiveWordConfig.java
Requerido, versión v0.0.15 y superior.
El pseudocódigo simplificado es el siguiente, y la fuente de los datos es una base de datos.
Myddwordallow y Myddworddeny son clases de implementación personalizadas basadas en bases de datos como fuente.
@ Configuration
public class SpringSensitiveWordConfig {
@ Autowired
private MyDdWordAllow myDdWordAllow ;
@ Autowired
private MyDdWordDeny myDdWordDeny ;
/**
* 初始化引导类
* @return 初始化引导类
* @since 1.0.0
*/
@ Bean
public SensitiveWordBs sensitiveWordBs () {
SensitiveWordBs sensitiveWordBs = SensitiveWordBs . newInstance ()
. wordAllow ( WordAllows . chains ( WordAllows . defaults (), myDdWordAllow ))
. wordDeny ( myDdWordDeny )
// 各种其他配置
. init ();
return sensitiveWordBs ;
}
}La inicialización del vocabulario sensible requiere mucho tiempo, por lo que se recomienda hacer la inicialización de inicio una vez que comience el programa.
Después de V0.6.0, agregue la prueba de referencia correspondiente.
BenchmarktimeStest
El entorno de prueba es un cuaderno normal:
处理器 12th Gen Intel(R) Core(TM) i7-1260P 2.10 GHz
机带 RAM 16.0 GB (15.7 GB 可用)
系统类型 64 位操作系统, 基于 x64 的处理器
PD: diferentes entornos variarán, pero la proporción es básicamente estable.
Datos de prueba: más de 100 cadenas, bucle 10W veces.
| Número de serie | Escena | pérdida de tiempo | Observación |
|---|---|---|---|
| 1 | Solo hacer palabras sensibles sin ninguna conversión de formato | 1470 ms, aproximadamente 7.2W QPS | En busca de un rendimiento extremo, puede configurarlo así |
| 2 | Solo hacer palabras sensibles, admitir todo el formato de conversión | 2744 ms, alrededor de 3.7W QPS | Cumplir con la mayoría de los escenarios |
Eliminar palabras sensibles de caracteres chinos individuales. En China, las frases deben considerarse como una sola palabra para reducir la tasa de juicio erróneo.
Admite cambios de palabras sensibles individuales?
¿Eliminar, agregar, editar?
Soporte de interfaz de etiqueta de palabras sensible
Soporte de etiqueta al procesar palabras confidenciales
Comparación de uso de memoria + Optimización de Datos de Word
Los usuarios especifican frases personalizadas y permiten obtener la combinación de frases especificadas, lo que lo hace más flexible
Formatcombine/checkcombine/Política de combinación de Permiso de Modenyycombina, lo que permite la personalización del usuario.
Optimización de la estrategia de verificación de palabras, transversal unificada + conversión
Agregue ThreadLocal y otras optimizaciones de rendimiento
Consola de palabras sensible de la palabra sensible v1.2.0 código abierto
¿Cómo admitir la implementación distribuida en la versión Sensitive-Word-Admin V1.3.0?
01-Beginner de la herramienta de palabras sensibles a la fuente abierta
02 ¿HOW para implementar una herramienta de palabra confidencial? Aclarar la idea de implementar palabras prohibidas
03 soporte de palabras Palabra de parada Optimización de palabras y símbolos especiales
04 Diccionario de palabras sensibles adelgazadas
05 explicación detallada del algoritmo de DFA de palabras sensibles (algoritmo de TRIE TREE)
06 Palabras sensibles (palabras sucias) ¿Cómo ignorar los personajes sin sentido? Lograr un mejor efecto de filtrado
V0.10.0 Soporte preliminar para la etiqueta de clasificación de palabras sucias
V0.11.0 - Nuevas características de palabras confidenciales: ignorar caracteres sin sentido, diccionario de etiqueta de palabras
V0.12.0 La capacidad de etiquetado de palabras/palabras sucias sensibles a los que se mejora aún más
V0.13.0 Versión de la función de palabra sensible a la versión admite una coincidencia de palabras completa en palabras en inglés
V0.16.1 Liberación de recursos de memoria del diccionario de nuevas características de palabras confidenciales
V0.19.0 - Palabras confidenciales con nuevas características de palabras sensibles editadas individualmente sin inicialización repetida
V0.20.0 Las nuevas características de las palabras confidenciales coinciden con todos los números, no los coincidencias parciales
V0.21.0 Whitelists con nuevas características de palabras confidenciales admite edición única, corrigiendo el problema cuando las listas blancas contienen listas negras
Pinyin a Pinyin
pinyin2hanzi pinyin a caracteres chinos
segmento de alto rendimiento segmentación de palabras chinas
OpenCc4j China tradicional chino Simplificado Simplificado Chino Conversión
NLP-Hanzi similar a la similitud de personaje chino
Detección de ortografía de comprobante de palabras
palabras sensibles