J'ai une obligation de filtrer le blasphème des soumissions des utilisateurs dans une application Web basée sur Java. Le client est conscient à la fois du problème Scunthorpe et du problème Clbuttic et en a accepté les conséquences. Je vous en prie, je ne souhaite pas un débat sur le bien-fondé de l'absence de censure.
Il y a deux bits de données:
- La soumission de l'utilisateur, qui peut potentiellement contenir environ 500 mots;
- Une table de base de données à colonne unique contenant des mots qui ne sont pas autorisés. Il peut y avoir plusieurs milliers d'enregistrements dans ce tableau.
La solution actuelle me semble fausse:
- La table entière est chargée dans une chaîne statique [] au démarrage dans un singleton (résidant ainsi en mémoire).
- Pour chaque soumission d'utilisateur, nous parcourons le tableau et faisons un .indexOf () pour voir si un mot donné dans la chaîne [] apparaît dans la soumission.
- S'il apparaît, nous le remplaçons par des caractères de style% $ # @%. Pour cela, vous devez tokeniser la soumission de l'utilisateur, parcourir (à nouveau) toute la soumission de l'utilisateur en tant que jetons et remplacer chaque instance du mot trouvé.
Il peut y avoir de l'éclat dans cette solution, mais je suis sceptique. Et après l'avoir regardé pendant un moment, je ne peux pas trouver mon chemin.
La question est de savoir quelle est la solution qui donnera de bonnes performances et, espérons-le, sera raisonnablement raisonnable pour les futurs développeurs à maintenir après que je sois viré pour avoir échoué à filtrer un mot obscur dont je n'ai jamais entendu parler?
Réponses:
La seule façon de faire un filtre de mots intelligemment est d'utiliser un système de correspondance phonétique. J'ai écrit un filtre de blasphème très efficace pour un jeu en ligne massivement multijoueur très populaire pour les préadolescents et les adolescents il y a quelques années à Java.
Il était basé sur un algorithme Double MetaPhone hautement modifié qui a été modifié pour être plus précis au lieu de la valeur par défaut qui doit correspondre à autant de choses que possible. Il était extrêmement efficace car il reprenait les fautes d'orthographe et l'orthographe phonétique exactement comme les mots réels. J'ai également ajouté
l33t
parler ettxt
parler de l'algorithme MetaPhone, ce qui en fait plus un algorithme de métaphone triple / quadruple.Il comprenait un pré-processeur qui compressait les lettres en cours d'exécution et détectait des choses comme les enfants,
w o r d s
en compressant intelligemment les lettres et en éliminant les doublons en cours d'exécutionwwoorrddss
, il était très spécialisé pour l'anglais uniquement.Il était assez rapide il y a 8 ans pour être utilisé dans un flux de système de chat en temps réel sans latence notable avec des dizaines de milliers d'utilisateurs sur un système CPU monocœur.
Nous avions une liste de mots qui étaient codés par métaphone dans une table de la base de données, et elle était chargée dans une carte statique qui était étonnamment petite et nous n'avons jamais eu à faire quoi que ce soit de spécial pour accéder à la liste des mots interdits, j'ai pu ajouter détection de phrases en utilisant les mêmes techniques pour presque gratuitement.
Bien sûr, j'avais un journal de toutes les conversations de milliers d'enfants essayant de briser le système en temps réel, j'avais donc un ensemble de données assez complet sur lequel travailler. La façon dont je me suis connecté était quand quelqu'un a déclenché le filtre avec un positif, j'ai enregistré les prochains messages de chat qui n'ont pas déclenché le filtre de leur part, de cette façon s'ils trouvaient un moyen de contourner un mot ou une phrase en particulier, je pourrais adapter mon système et attraper ça. J'étais assez à l'épreuve des balles après seulement quelques semaines.
la source
Si vous voulez faire la correspondance efficacement, l' algorithme Aho Corasick est une assez bonne option (je suis sûr que vous pouvez trouver une implémentation Java flottante).
Bien sûr, vous voudrez probablement prétraiter la soumission pour remplacer toute irrégularité d'orthographe ('$' -> 's', '@' -> 'a', '| <' -> 'k', etc.)
la source
Au lieu de charger dans une chaîne statique [], utilisez le HashMap [] ou un autre type d'arbre binaire (si vous souhaitez améliorer la recherche) faisant de la chaîne votre clé dans le hachage. Divisez votre chaîne par des espaces et supprimez la ponctuation. Ensuite, vous pouvez interroger le HashMap pour chaque mot de votre chaîne de partage; si le hashmap revient avec non null alors vous savez que vous avez un mauvais mot.
La chose qui échoue ici est le problème Clbuttic où quelqu'un ajoute des caractères aléatoires autour du mauvais mot ex.
bhassda
la source
L'utilisation d'un système phonique n'est pas la seule solution, mais ce pourrait être la plus simple car il existe de nombreuses bibliothèques open source qui font ce genre de choses.
La partie difficile sera toujours la partie correspondante de tout algorithme et il semble que votre correspondance soit assez lente et naïve. Vous ne pouvez pas supposer que indexOf correspondra correctement sans une forme de vérification auxiliaire.
De plus, vous finirez par faire une boucle sur l'ensemble de la chaîne N fois, où N est le nombre de mots sur votre liste noire. Les suggestions d'utilisation de Set ou HashMap vont certainement améliorer quelque peu les choses.
Dans la plupart des cas, un algorithme basé sur un état linéaire est le meilleur et le plus rapide. J'ai écrit la solution pour Clean Speak et elle utilise ce type d'algorithme avec un système de correspondance phonique de pré-processus. C'était la seule solution qui ne s'est pas compliquée lorsque le blasphème est intégré (si foo est blasphème, l'incorporation est foosucker) et a pu conserver un niveau élevé de performances. Il évolue également bien pour d'autres langages sans implémentations de nouveaux codex.
Enfin, le prétraitement de tout formulaire est généralement à éviter. Dans la plupart des cas, vous pouvez faire la même chose de manière linéaire en manipulant chacun des caractères de la chaîne.
Bien sûr, je suggérerais d'étudier d'autres solutions à long terme, car dans la plupart des applications, la gestion du contenu généré par l'utilisateur est plus complexe que le simple filtrage grossier. Souvent, vous souhaitez également filtrer les informations personnelles telles que les e-mails et les numéros de sécurité sociale et parfois des choses comme les URL. De plus, nous avons constaté que la plupart des applications ont besoin d'une certaine forme de système de modération et de recherche de contenu. Celles-ci augmentent considérablement la complexité.
la source
Ce que vous voulez faire dans un cas comme celui-ci est de déterminer laquelle des deux listes de mots est la plus petite. Supposons que votre liste "verboten" contienne 2 000 mots et que la soumission maximale de l'utilisateur soit 500 mots. Dans ce cas, vous parcourrez la liste des mots dans la soumission de l'utilisateur et les rechercherez un par un dans la liste des mots interdits et vice versa.
L'autre changement que je ferais est que vous ne conservez pas la liste des mots interdits dans une chaîne [] - si vous recherchez dans le tableau, vous avez une recherche O (n) par mot dans la soumission de l'utilisateur. C'est assez mauvais. J'essaierais de mettre la structure de données que vous recherchez dans une sorte de conteneur associatif ou une structure arborescente qui a une meilleure performance de recherche (log n au lieu de n). Le défi ici serait que si vous placez la soumission de l'utilisateur dans ce conteneur, vous devrez garder une trace de la position du mot afin de pouvoir soit reconstruire l'entrée, soit mettre à jour la chaîne d'entrée si vous avez un résultat de recherche.
la source