Tengo un requisito para filtrar las malas palabras de los envíos de los usuarios en una aplicación web basada en Java. El cliente conoce tanto el problema de Scunthorpe como el problema de Clbuttic y ha aceptado las consecuencias. Por favor, no deseo un debate sobre los méritos de la falta de censura.
Hay dos bits de datos:
- El envío del usuario, que potencialmente puede contener aproximadamente 500 palabras;
- Una tabla de base de datos de una sola columna que contiene palabras que no están permitidas. Puede haber muchos miles de registros en esta tabla.
La solución actual me parece incorrecta:
- La tabla completa se carga en una cadena estática [] al inicio en un Singleton (por lo tanto, reside en la memoria).
- Para cada envío de usuario, recorremos la matriz y hacemos un .indexOf () para ver si alguna palabra dada en la Cadena [] aparece en el envío.
- Si aparece, lo reemplazamos con% $ # @% - caracteres de estilo. Esto se realiza mediante la tokenización del envío del usuario, recorriendo todo el envío del usuario como tokens (nuevamente) y reemplazando cada instancia de la palabra encontrada.
Puede haber brillo en esta solución, pero soy escéptico. Y después de haberlo mirado por un tiempo, no puedo encontrar mi camino más allá.
La pregunta es, ¿cuál es una solución que proporcionará un buen rendimiento y esperamos que sea razonablemente razonable que los futuros desarrolladores mantengan después de que me despidan por no filtrar alguna palabra oscura de la que nunca he oído hablar?
Respuestas:
La única forma de hacer un filtro de palabras de manera inteligente es usar un sistema de correspondencia fonética. Escribí un filtro de blasfemias muy efectivo para un juego en línea multijugador masivo muy popular para preadolescentes y adolescentes hace unos años en Java.
Se basó en un algoritmo Double MetaPhone altamente modificado que se ajustó para ser más preciso en lugar del valor predeterminado, que es hacer coincidir tantas cosas como sea posible. Fue extremadamente efectivo ya que detectó faltas de ortografía y deletreo fonético de la misma manera que las palabras reales. Añadí
l33t
hablar ytxt
hablar con el algoritmo de metaphone así, lo que hace más de un algoritmo Triple / Quad Metaphone.Presentaba un preprocesador que comprimía las letras en ejecución y detectaba cosas como que los niños pusieran cosas
w o r d s
al comprimir inteligentemente las letras juntas y eliminaba los duplicados en ejecuciónwwoorrddss
, era muy especializado solo en inglés.Hace 8 años, fue lo suficientemente rápido como para usarse en un flujo de sistema de chat en tiempo real sin latencia notable con decenas de miles de usuarios en un sistema de CPU de un solo núcleo.
Teníamos una lista de palabras codificadas por Metaphone en una tabla de la base de datos, y estaba cargada en un Mapa estático que era sorprendentemente pequeño y nunca tuvimos que hacer nada especial para acceder a la lista de palabras prohibidas, pude agregar detección de frases utilizando las mismas técnicas de forma casi gratuita.
Por supuesto, tenía un registro de todos los chats de miles de niños que intentaban romper el sistema en tiempo real, así que tenía un conjunto de datos bastante completo para trabajar. La forma en que hice el registro fue cuando alguien activó el filtro con un positivo, registré los siguientes mensajes de chat que no activaron el filtro , de esa manera, si encontraban una forma de evitar una palabra o frase en particular, podría adaptar mi sistema y atrapar eso. Estaba a prueba de balas después de solo un par de semanas.
fuente
Si desea hacer la correspondencia de manera eficiente, el algoritmo Aho Corasick es una opción bastante buena (estoy seguro de que puede encontrar una implementación de Java flotando).
Por supuesto, es probable que desee preprocesar el envío para reemplazar cualquier irregularidad ortográfica ('$' -> 's', '@' -> 'a', '| <' -> 'k', etc.)
fuente
En lugar de cargar en una Cadena estática [], utilice el HashMap [] o algún otro tipo de árbol binario (si desea mejorar la búsqueda) haciendo de la cadena su clave en el hash. Divide tu cadena por espacios y elimina la puntuación. Luego puede consultar el HashMap para cada palabra en su división de cadena; si el hashmap vuelve con no nulo, entonces sabes que tienes una mala palabra.
Lo que falla aquí es el problema Clbuttic donde alguien agrega caracteres aleatorios alrededor de la palabra mala ex.
bhassda
fuente
El uso de un sistema fónico no es la única solución de ninguna manera, pero podría ser la más simple ya que hay muchas bibliotecas de código abierto que hacen ese tipo de cosas.
La parte difícil siempre será la parte correspondiente de cualquier algoritmo y parece que su coincidencia es bastante lenta e ingenua. No puede suponer que indexOf coincidirá correctamente sin alguna forma de verificación auxiliar.
Además, terminarás recorriendo toda la cadena N veces, donde N es el número de palabras en tu lista negra. Las sugerencias para usar Set o HashMap definitivamente mejorarán un poco las cosas.
En la mayoría de los casos, un algoritmo basado en estado lineal es el mejor y más rápido. Escribí la solución para Clean Speak y utiliza este tipo de algoritmo con un sistema de coincidencia fonética previo al proceso. Esta fue la única solución que no se complicó cuando se profanó blasfemias (si foo es blasfemias, la incrustación es foosucker) y pudo mantener un alto nivel de rendimiento. También se escala muy bien para otros idiomas sin implementaciones de nuevos códices.
Por último, el preprocesamiento de cualquier forma generalmente es algo que debe evitarse. En la mayoría de los casos, puede hacer lo mismo de forma lineal mientras maneja cada uno de los caracteres de la cadena.
Por supuesto, sugeriría buscar otras soluciones a largo plazo porque en la mayoría de las aplicaciones el manejo de contenido generado por el usuario es más complejo que el simple filtrado de malas palabras. A menudo, también desea filtrar información personal como correos electrónicos y números de seguridad social y, a veces, cosas como URL. Además, hemos descubierto que la mayoría de las aplicaciones necesitan algún tipo de sistema de moderación y búsqueda de contenido. Estos aumentan la complejidad considerablemente.
fuente
Lo que quiere hacer en un caso como este es determinar cuál de las dos listas de palabras es la más pequeña. Digamos que su lista "verboten" contiene 2000 palabras y el envío máximo de usuarios es de 500 palabras. En ese caso, recorrerá la lista de palabras en el envío del usuario y las buscará una por una en la lista de palabras prohibidas y viceversa.
El otro cambio que haría es que no mantienes la lista de palabras prohibidas en una Cadena []: si buscas en la matriz, tienes una búsqueda O (n) por palabra en el envío del usuario. Eso está muy mal. Intentaría poner la estructura de datos que está buscando en algún tipo de contenedor asociativo o estructura de árbol que tenga un mejor rendimiento de búsqueda (log n en lugar de n). El desafío aquí sería que si coloca el envío del usuario en este contenedor, tendrá que realizar un seguimiento de la posición de la palabra para que pueda reconstruir la entrada o actualizar la cadena de entrada si tiene un resultado de búsqueda.
fuente