Profanity Filter Performance in Java

9

Ho l'obbligo di filtrare le parolacce dagli invii degli utenti in un'applicazione web basata su Java. Il client è a conoscenza sia del Scunthorpe Problem che del Clbuttic Problem e hanno accettato le conseguenze. Per favore, non desidero un dibattito sul merito della mancanza della censura.

Ci sono due bit di dati:

  1. L'invio dell'utente, che può potenzialmente contenere circa 500 parole;
  2. Una tabella di database a colonna singola contenente parole non consentite. Ci possono essere molte migliaia di record in questa tabella.

La soluzione attuale mi sembra sbagliata:

  1. L'intera tabella viene caricata in una stringa statica [] all'avvio in un Singleton (quindi residente in memoria).
  2. Per ogni invio di un utente eseguiamo un ciclo attraverso la matrice e facciamo un .indexOf () per vedere se una determinata parola nella stringa [] appare nella submission.
  3. Se appare, sostituiremo con% $ # @% - caratteri di stile. Ciò viene effettuato mediante tokenizzazione dell'invio dell'utente, eseguendo il loop dell'intero invio dell'utente come token (di nuovo) e sostituendo ogni istanza della parola trovata.

Potrebbe esserci una brillantezza in questa soluzione, ma sono scettico. E dopo averlo guardato per un po 'non riesco a trovare la via per superarlo.

Le domande sono, qual è una soluzione che darà buone prestazioni e, si spera, ragionevolmente ragionevole per gli sviluppatori futuri da mantenere dopo essere stato licenziato per non aver filtrato alcune parole oscure di cui non ho mai sentito parlare?

    
posta blueishgoldfish 09.07.2011 - 03:19
fonte

5 risposte

18

L'unico modo per fare un filtro di parole in modo intelligente è utilizzare un sistema di corrispondenza fonica. Ho scritto un filtro di profanità molto efficace per un gioco online multigiocatore molto popolare per i tweens e gli adolescenti pochi anni fa in Java.

Era basato su un algoritmo Double MetaPhone altamente modificato che è stato ottimizzato per di più preciso al posto del default che corrisponde a quante più cose possibili. È stato così estremamente efficace da quando ha analizzato le ortografie sbagliate e le grafie fonetiche esattamente come le parole reali. Ho aggiunto l33t speak e txt parlano anche dell'algoritmo del MetaPhone, rendendolo più simile a un algoritmo Triple / Quad Metaphone.

Presentava un pre-processore che comprendeva le lettere in esecuzione e rilevava cose come i bambini mettendo cose come w o r d s comprimendo le lettere in modo intelligente ed eliminando duplicati come wwoorrddss , era molto specializzato solo per l'inglese.

È stato abbastanza veloce 8 anni fa per essere utilizzato in un flusso di sistema di chat in tempo reale senza alcuna latenza visibile con decine di migliaia di utenti su un sistema CPU core singolo.

Avevamo una lista di parole che erano codificate da Metaphone in una tabella nel database, ed è stata caricata in una mappa statica che era sorprendentemente piccola e non abbiamo mai dovuto fare niente di speciale per accedere all'elenco delle parole vietate, ero in grado di aggiungere il rilevamento di frasi usando le stesse tecniche quasi gratis.

Naturalmente ho avuto un log in esecuzione di tutte le chat di migliaia di ragazzi che cercavano di rompere il sistema in tempo reale, quindi avevo un insieme di dati piuttosto complesso su cui lavorare. Il modo in cui ho fatto il logging è stato quando qualcuno ha attivato il filtro con un positivo, ho registrato i messaggi di chat successivi che non hanno attivato il filtro da loro, in questo modo se hanno trovato il modo di aggirare un particolare parola o frase, potrei adattare il mio sistema e prenderlo. Ero abbastanza a prova di proiettili dopo solo un paio di settimane.

    
risposta data 09.07.2011 - 03:35
fonte
2

Se vuoi eseguire la corrispondenza in modo efficiente, l'algoritmo Aho Corasick è abbastanza buono opzione (sono sicuro che puoi trovare un'implementazione Java mobile).

Ovviamente probabilmente vorrai elaborare l'invio per sostituire eventuali irregolarità di ortografia ('$' - > 's', '@' - > 'a', '| <' - > 'k', ecc.)

    
risposta data 09.07.2011 - 03:46
fonte
0

Invece di caricare in una stringa statica [], utilizzare HashMap [] o qualche altro tipo di albero binario (se si desidera migliorare la ricerca) rendendo la stringa chiave nell'hash. Dividi la tua stringa in base agli spazi e rimuovi la punteggiatura. Quindi puoi interrogare la HashMap per ogni parola nella divisione della stringa; se l'hashmap ritorna con un valore non nullo, allora sai di avere una parolaccia.

La cosa che fallisce qui è il problema di Clbuttic in cui qualcuno aggiunge caratteri casuali attorno alla parolaccia ex. bhassda

    
risposta data 09.07.2011 - 03:34
fonte
-1

L'uso di un sistema fonico non è l'unica soluzione con qualsiasi mezzo, ma potrebbe essere il più semplice poiché ci sono un sacco di librerie open source che fanno questo genere di cose.

La parte difficile sarà sempre la parte corrispondente di qualsiasi algoritmo e sembra che la tua corrispondenza sia piuttosto lenta e ingenua. Non si può presumere che indexOf corrisponda correttamente senza una qualche forma di controllo ausiliario.

Inoltre, finirai per eseguire il loop sull'intero String N volte, dove N è il numero di parole sulla tua lista nera. I suggerimenti per utilizzare Set o HashMap miglioreranno sicuramente le cose.

Nella maggior parte dei casi, un algoritmo basato sullo stato lineare è il migliore e il più veloce. Ho scritto la soluzione per Clean Speak e utilizza questo tipo di algoritmo con un sistema di corrispondenza fonica pre-processo. Questa era l'unica soluzione che non si è complicata quando la profanità è incorporata (se foo è blasfemo, l'incorporamento è foosucker) ed è stato in grado di mantenere un alto livello di prestazioni. Si adatta anche alle altre lingue senza implementazioni di nuovi codici.

Infine, la pre-elaborazione di qualsiasi forma è generalmente qualcosa da evitare. Nella maggior parte dei casi puoi fare la stessa cosa in modo lineare man mano che gestisci ognuno dei caratteri nella stringa.

Naturalmente, suggerirei di guardare ad altre soluzioni a lungo termine perché nella maggior parte delle applicazioni la gestione dei contenuti generati dagli utenti è più complessa del semplice filtraggio di profanità. Spesso si desidera anche filtrare le informazioni personali come e-mail e numeri di previdenza sociale e talvolta cose come gli URL. Inoltre, abbiamo scoperto che la maggior parte delle applicazioni richiede una qualche forma di sistema di moderazione e ricerca di contenuti. Questi aumentano considerevolmente la complessità.

    
risposta data 13.07.2011 - 17:11
fonte
-2

Quello che vuoi fare in un caso come questo è determinare quale dei due elenchi di parole è quello più piccolo. Supponi che la tua lista "verboten" contenga 2000 parole e l'invio massimo da parte dell'utente è di 500 parole. In tal caso, dovrai scorrere l'elenco di parole nell'invio dell'utente e cercarle una alla volta nell'elenco delle parole vietate e viceversa.

L'altro cambiamento che farei è che non mantieni la lista delle parole proibite in una stringa [] - se cerchi nell'array hai una ricerca O (n) per parola nell'invio dell'utente. È piuttosto brutto. Cercherò di mettere la struttura dei dati che stai cercando in una sorta di contenitore associativo o struttura ad albero che abbia una migliore prestazione di ricerca (log n invece di n). La sfida qui è che se metti l'invio dell'utente in questo contenitore, dovrai tenere traccia della posizione della parola in modo da poter ricostruire l'input o aggiornare la stringa di input se hai un hit di ricerca.

    
risposta data 09.07.2011 - 03:35
fonte

Leggi altre domande sui tag