Ich muss die Profanität aus den Einsendungen der Benutzer in einer Java-basierten Webanwendung herausfiltern. Der Kunde ist sich sowohl des Scunthorpe-Problems als auch des Clbuttic-Problems bewusst und hat die Konsequenzen akzeptiert. Bitte, ich wünsche mir keine Debatte über das Fehlen einer Zensur.
Es gibt zwei Datenbits:
- Die Übermittlung des Benutzers, die möglicherweise 500 Wörter oder so enthalten kann;
- Eine einspaltige Datenbanktabelle mit Wörtern, die nicht zulässig sind. Diese Tabelle enthält möglicherweise viele tausend Datensätze.
Die vorliegende Lösung scheint mir falsch:
- Die gesamte Tabelle wird beim Start in einen Singleton (also im Speicher) in einen statischen String [] geladen.
- Für jede Benutzerübermittlung durchlaufen wir das Array und führen eine .indexOf () durch, um festzustellen, ob ein bestimmtes Wort in der Zeichenfolge [] in der Übermittlung erscheint.
- Wenn es erscheint, ersetzen wir durch Zeichen im Stil von $ $ # @%. Dies erfolgt durch Tokenisieren der Benutzerübermittlung, Durchlaufen der gesamten Benutzerübermittlung als Token (erneut) und Ersetzen jeder Instanz des gefundenen Wortes.
Diese Lösung mag brillant sein, aber ich bin skeptisch. Und nachdem ich es mir eine Weile angesehen habe, kann ich mich nicht daran vorbei finden.
Die Frage ist, was ist eine Lösung, die eine gute Leistung erbringt und hoffentlich für zukünftige Entwickler einigermaßen vernünftig ist, wenn ich entlassen werde, weil ich kein obskures Wort herausgefiltert habe, von dem ich noch nie gehört habe?
Antworten:
Die einzige Möglichkeit, einen Wortfilter intelligent durchzuführen, ist die Verwendung eines Phonic-Matching-Systems. Ich habe vor einigen Jahren in Java einen sehr effektiven Obszönitätsfilter für ein sehr beliebtes Online-Spiel für Tweens und Teens mit mehreren Spielern geschrieben.
Es basierte auf einem stark modifizierten Double MetaPhone- Algorithmus, der so optimiert wurde, dass er genauer ist als der Standard, der so vielen Dingen wie möglich entspricht. Es war so äußerst effektiv, da es falsche und phonetische Schreibweisen genauso wie die tatsächlichen Wörter auffing. Ich habe auch
l33t
Sprechen undtxt
Sprechen mit dem MetaPhone-Algorithmus hinzugefügt , wodurch er eher zu einem Triple / Quad-Metaphon-Algorithmus wird.Es gab einen Vorprozessor, der laufende Buchstaben komprimierte und Dinge wie die Kinder erkannte, die Dinge zusammenstellten,
w o r d s
indem sie die Buchstaben intelligent zusammen komprimierten und laufende Duplikate eliminiertenwwoorrddss
. Es war nur auf Englisch spezialisiert.Es war vor 8 Jahren schnell genug, um in einem Echtzeit-Chat-System-Stream ohne merkliche Latenz mit Zehntausenden von Benutzern auf einem Single-Core-CPU-System verwendet zu werden.
Wir hatten eine Liste von Wörtern, die Metaphone-codiert waren, in einer Tabelle in der Datenbank, und sie wurde in eine statische Karte geladen, die überraschend klein war, und wir mussten nie etwas Besonderes tun, um auf die Liste der gesperrten Wörter zuzugreifen, konnte ich hinzufügen Phrasenerkennung mit den gleichen Techniken fast kostenlos.
Natürlich hatte ich ein laufendes Protokoll aller Chats von Tausenden von Kindern, die versuchten, das System in Echtzeit zu beschädigen, sodass ich einen ziemlich umfassenden Datensatz hatte, gegen den ich arbeiten konnte. So wie ich die Protokollierung tat , war , wenn jemand die Filter mit einem positiven ausgelöst, ich die nächsten paar Chat - Nachrichten protokollierte , die nicht die Filter von ihnen auslösen, dass die Art und Weise , wenn sie einen Weg , um ein bestimmtes Wort oder einen Satz finden sind, konnte ich Passen Sie mein System an und fangen Sie das. Nach nur ein paar Wochen war ich ziemlich kugelsicher.
quelle
Wenn Sie den Abgleich effizient durchführen möchten, ist der Aho Corasick- Algorithmus eine ziemlich gute Option (ich bin sicher, dass Sie eine Java-Implementierung finden können).
Natürlich möchten Sie die Übermittlung wahrscheinlich vorverarbeiten, um Rechtschreibunregelmäßigkeiten zu ersetzen ('$' -> 's', '@' -> 'a', '| <' -> 'k' usw.).
quelle
Anstatt in einen statischen String [] zu laden, verwenden Sie die HashMap [] oder einen anderen Typ von Binärbaum (wenn Sie die Suche verbessern möchten), um den String zu Ihrem Schlüssel im Hash zu machen. Teilen Sie Ihren String durch Leerzeichen und entfernen Sie Satzzeichen. Anschließend können Sie die HashMap für jedes Wort in Ihrer Zeichenfolgenaufteilung abfragen. Wenn die Hashmap mit nicht null zurückkommt, wissen Sie, dass Sie ein schlechtes Wort haben.
Die Sache, die hier fehlschlägt, ist das Clbuttic-Problem, bei dem jemand zufällige Zeichen um das schlechte Wort ex hinzufügt.
bhassda
quelle
Die Verwendung eines Phonic-Systems ist keineswegs die einzige Lösung, aber möglicherweise die einfachste, da es viele Open-Source-Bibliotheken gibt, die solche Aufgaben ausführen.
Der schwierige Teil wird immer der passende Teil eines Algorithmus sein und es klingt so, als ob Ihre Übereinstimmung ziemlich langsam und naiv ist. Sie können nicht davon ausgehen, dass indexOf ohne irgendeine Form der Hilfsprüfung korrekt übereinstimmt.
Außerdem durchlaufen Sie den gesamten String N-mal, wobei N die Anzahl der Wörter auf Ihrer schwarzen Liste ist. Die Vorschläge zur Verwendung von Set oder HashMap werden die Dinge definitiv etwas verbessern.
In den meisten Fällen ist ein linearer zustandsbasierter Algorithmus am besten und schnellsten. Ich habe die Lösung für Clean Speak geschrieben und sie verwendet diese Art von Algorithmus mit einem Phonic-Matching-System vor dem Prozess. Dies war die einzige Lösung, die nicht kompliziert wurde, wenn Profanität eingebettet wurde (wenn foo Profanität ist, ist Einbettung Foosucker) und ein hohes Leistungsniveau beibehalten konnte. Es lässt sich auch gut für andere Sprachen skalieren, ohne dass neue Codexe implementiert werden müssen.
Schließlich ist eine Vorverarbeitung jeglicher Form im Allgemeinen zu vermeiden. In den meisten Fällen können Sie das Gleiche linear tun, während Sie mit den einzelnen Zeichen in der Zeichenfolge umgehen.
Natürlich würde ich vorschlagen, langfristig nach anderen Lösungen zu suchen, da in den meisten Anwendungen die Verarbeitung von benutzergenerierten Inhalten komplexer ist als nur die Filterung von Obszönitäten. Oft möchten Sie auch persönliche Informationen wie E-Mails und Sozialversicherungsnummern und manchmal auch URLs filtern. Außerdem haben wir festgestellt, dass die meisten Anwendungen eine Art Moderationssystem und Inhaltssuche benötigen. Diese erhöhen die Komplexität erheblich.
quelle
In einem solchen Fall möchten Sie feststellen, welche der beiden Wortlisten die kleinere ist. Angenommen, Ihre "verbotene" Liste enthält 2000 Wörter und die maximale Benutzerübermittlung beträgt 500 Wörter. In diesem Fall durchlaufen Sie die Liste der Wörter in der Benutzerübermittlung und suchen sie einzeln in der Liste der verbotenen Wörter nach und umgekehrt.
Die andere Änderung, die ich vornehmen würde, ist, dass Sie die Liste der verbotenen Wörter nicht in einem String [] aufbewahren. Wenn Sie im Array suchen, haben Sie eine O (n) -Suche pro Wort in der Benutzerübermittlung. Das ist ziemlich schlimm Ich würde versuchen, die Datenstruktur, in der Sie suchen, in eine Art assoziativen Container- oder Baumstruktur zu integrieren, der eine bessere Suchleistung aufweist (log n statt n). Die Herausforderung hierbei wäre, dass Sie, wenn Sie die Benutzerübermittlung in diesen Container einfügen, die Wortposition verfolgen müssen, damit Sie entweder die Eingabe rekonstruieren oder die Eingabezeichenfolge aktualisieren können, wenn Sie einen Suchtreffer haben.
quelle