Prestazioni del filtro volgarità in Java


9

Ho l'obbligo di filtrare le volgarità dagli invii degli utenti in un'applicazione Web basata su Java. Il cliente è a conoscenza sia del Scunthorpe problema e la Clbuttic problema e hanno accettato le conseguenze. Per favore, non desidero un dibattito sul merito della mancanza della censura.

Esistono due bit di dati:

  1. L'invio dell'utente, che può contenere potenzialmente 500 parole o giù di lì;
  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 risiede in memoria).
  2. Per ogni invio utente eseguiamo il ciclo attraverso l'array e facciamo un .indexOf () per vedere se una qualsiasi parola nella stringa [] appare nell'invio.
  3. Se appare, sostituiamo con% $ # @% - caratteri di stile. Questo viene fatto tokenizzando l'invio dell'utente, eseguendo il ciclo dell'intero invio dell'utente come token (di nuovo) e sostituendo ogni istanza della parola trovata.

Potrebbe esserci brillantezza in questa soluzione, ma sono scettico. E dopo averlo guardato per un po 'non riesco a trovarlo.

Le domande sono: qual è una soluzione che darà buone prestazioni e si spera sia ragionevolmente ragionevole per i futuri sviluppatori da mantenere dopo che sono stato licenziato per non riuscire a filtrare qualche parola oscura di cui non ho mai sentito parlare?


Dici che ti sembra sbagliato, senza dirci perché pensi che sia sbagliato. Quindi chiedi una soluzione performante, senza dircelo, in che modo la soluzione attuale non è sufficiente. Quanti messaggi al secondo ricevi, quanti di essi puoi elaborare?
utente sconosciuto

Ho pensato che la soluzione fosse sbagliata, principalmente perché la base di codice in cui sto lavorando è inadeguata e sciatta. Data la mia propensione, non mi fidavo della mia stessa sfiducia. Ho sentito che l'opinione degli altri sarebbe stata utile. Le cose che hanno scatenato gli allarmi per me sono state String [] (che cos'è questo 1999?), Che si sovrapponeva al molto grande String [] anziché al set di dati molto più piccolo che l'utente invia, annidando un loop all'interno del loop String [] con invio utente tokenizzato e così via. L'utilizzo previsto non è specificato, idealmente una soluzione elegante con prestazioni ragionevoli sarebbe adorabile.
blueishgoldfish,

2
"Prestazioni ragionevoli" può significare qualsiasi cosa. Se non hai un obiettivo concreto, non puoi sapere se lo hai raggiunto. Se si accelera un processo, in modo che sia 100 volte più veloce, è questo un obiettivo? Se l'utente sta aspettando 1ms o 1 / 10s? L'utente non trarrà beneficio dal tuo lavoro.
utente sconosciuto

Risposte:


18

L'unico modo per fare un filtro di parole in modo intelligente è usare un sistema di corrispondenza fonica. Ho scritto un filtro volgare molto efficace per un popolare gioco online multigiocatore di massa molto popolare per ragazzi e adolescenti qualche anno fa in Java.

Si basava su un algoritmo Double MetaPhone altamente modificato che è stato ottimizzato per essere più accurato invece del valore predefinito che deve corrispondere al maggior numero di cose possibile. È stato estremamente efficace poiché ha raccolto errori di ortografia e ortografici proprio come le parole reali. Ho aggiunto anche l33tparlare e txtparlare con l'algoritmo MetaPhone, rendendolo più un algoritmo Triple / Quad Metaphone.

Presenta un pre-processore che comprime le lettere correnti e rileva cose come i bambini che mettono le cose w o r d scomprimendo in modo intelligente le lettere insieme ed eliminando i duplicati in esecuzione wwoorrddss, era molto specializzato solo in inglese.

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

Avevamo un elenco di parole codificate da Metaphone in una tabella nel database, ed era caricata in una mappa statica sorprendentemente piccola e non abbiamo mai dovuto fare nulla di speciale per accedere all'elenco di parole vietate, sono stato in grado di aggiungere rilevamento delle frasi usando le stesse tecniche quasi gratis.

Ovviamente avevo un registro di tutte le chat di migliaia di bambini che cercavano di rompere il sistema in tempo reale, quindi avevo una serie piuttosto completa di dati su cui lavorare. Il modo in cui ho effettuato la registrazione è stato quando qualcuno ha attivato il filtro con un positivo, ho registrato i successivi messaggi di chat che non hanno attivato il filtro da loro, in questo modo se avessero trovato un modo per aggirare una determinata parola o frase, avrei potuto adatta il mio sistema e prendilo. Ero abbastanza a prova di proiettile dopo solo un paio di settimane.


3
Questa soluzione sembra essere la migliore. Il problema è (o era a questo punto) che ho dovuto risolverlo in un pomeriggio. Se c'è tempo sufficiente, prenderò l'approccio Double MetaPhone o ti assumerò per farlo. :-)
blueishgoldfish

Quindi, immagino che metà delle persone smetterà di giocare ora: D
Davor Ždralo,

2

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

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


Proprio quello che stavo cercando, grazie! Ecco un'implementazione Java: hkn.eecs.berkeley.edu/~dyoo/java
Remi Mélisson

0

Invece di caricare in una stringa statica [], utilizza HashMap [] o qualche altro tipo di albero binario (se vuoi migliorare la ricerca) rendendo la stringa la tua chiave nell'hash. Dividi la stringa per spazi e rimuovi la punteggiatura. Quindi è possibile eseguire una query su HashMap per ogni parola nella suddivisione della stringa; se l'hashmap ritorna con un valore nullo allora sai che hai una parolaccia.

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


Penso che l'ultimo avvertimento sia ciò che rende questa soluzione praticamente inutile: non c'è modo di estenderla a tutto tranne che alle corrispondenze di parole intere.

Questa è una dichiarazione equa; ma diventa difficile catturare ogni cosa possibile che la mente umana possa inventare per sfuggire a un filtro volgare. Puoi sempre creare un'enorme espressione regolare con le istruzioni OR per combinare tutte le opzioni e quindi abbinare la regex con l'input. O potresti fare una selezione dal database con il "campo parolacce" dal database con un RLIKE contro l'input. Return indica una parolaccia e restituisce anche la parolaccia.

@Suroot non è difficile catturare praticamente qualsiasi parola o frase con corrispondenza fonetica come la mia domanda parla. Gli abbinamenti assoluti non funzioneranno mai o si ridimensioneranno, ma l'abbinamento fonetico funziona quasi il 100% delle volte una volta regolato il più possibile.

-1

L'uso di un sistema fonico non è l'unica soluzione in alcun modo, ma potrebbe essere la più semplice poiché ci sono molte librerie open source che fanno quel genere di cose.

La parte difficile sarà sempre la parte corrispondente di qualsiasi algoritmo e sembra che la tua partita sia piuttosto lenta e ingenua. Non puoi presumere che indexOf corrisponderà correttamente senza una qualche forma di controllo ausiliario.

Inoltre, finirai per ripetere l'intero ciclo N volte, dove N è il numero di parole nella tua lista nera. I suggerimenti per usare 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 abbinamento fonico pre-process. Questa è stata l'unica soluzione che non si è complicata quando la volgarità è incorporata (se foo è volgarità, l'incorporamento è foosucker) ed è stato in grado di mantenere un alto livello di prestazioni. Si adatta anche bene per 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 mentre gestisci ciascuno dei caratteri nella stringa.

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


-2

Quello che vuoi fare in un caso come questo è determinare quale delle due liste di parole è quella più piccola. Supponiamo che la tua lista "verboten" contenga 2000 parole e che l'invio massimo dell'utente sia di 500 parole. In tal caso, scorrerai l'elenco delle parole nella presentazione dell'utente e le cercherai una per una nell'elenco delle parole proibite e viceversa.

L'altra modifica che vorrei apportare è che non conservi l'elenco di parole proibite in una stringa [] - se cerchi nell'array hai una ricerca O (n) per parola nell'invio dell'utente. È piuttosto brutto. Proverei a mettere la struttura di dati in cui stai cercando in una sorta di contenitore associativo o struttura ad albero che abbia prestazioni di ricerca migliori (accedi a n invece di n). La sfida qui sarebbe 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.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.