2010-10-05 25 views
5

C'è un elenco di parole vietate (o stringhe per essere più generali) e un altro elenco con diciamo utenti mail. Vorrei ritirare tutte le parole vietate da tutte le mail.Come tagliare le parole specificate dalla stringa

esempio banale:

foreach(string word in wordsList) 
{ 
    foreach(string mail in mailList) 
    { 
     mail.Replace(word,String.Empty); 
    } 
} 

Come posso migliorare questo algoritmo?


Grazie per i consigli. Ho votato poche risposte ma non ho contrassegnato nessuna risposta in quanto era più una discussione che una soluzione. Alcune persone mancavano parole vietate con parolacce. Nel mio caso non devo preoccuparmi di riconoscere 'sh1t' o qualcosa del genere.

+10

Si riscontrano problemi di prestazioni con questo? Non ottimizzare fino a quando necessario. – Oded

+1

Non ho problemi di prestazioni. Voglio solo imparare e migliorare le mie capacità :-) – zgorawski

risposta

2

Si potrebbe utilizzare RegEx per rendere le cose un po 'più pulito:

var bannedWords = @"\b(this|is|the|list|of|banned|words)\b"; 

foreach(mail in mailList) 
    var clean = Regex.Replace(mail, bannedWords, "", RegexOptions.IgnoreCase); 

Anche questo, però, è ben lungi dall'essere perfetto dal momento che la gente sarà sempre trovare un modo per aggirare qualsiasi tipo di filtro.

+0

Questo non sta rimuovendo le parole vietate, sta rimuovendo sottostringhe vietate.Ad esempio, questo cambierebbe la parola "spesso" in una stringa in "dieci". –

+0

@ Michael - Ovviamente il mio RegEx-Fu non è all'altezza. Ho aggiunto quello che pensavo fosse il modo giusto per limitare i confini delle parole. Eventuali correzioni? –

+0

Sembra meglio, grazie. Anche se citerò di nuovo (come sotto) che probabilmente non è l'ideale per fare un Regex come questo se la tua lista è più di poche decine di parole. –

5

Gli approcci semplici al filtraggio di profanità non funzioneranno - gli approcci complessi non funzionano, per la maggior parte, neanche.

Cosa succede quando ottieni un lavoro come "password" e vuoi filtrare "ass"? Cosa succede quando una persona intelligente scrive 'a $$' invece - l'intento è ancora chiaro, giusto?

Vedere How do you implement a good profanity filter? per un'ampia discussione.

+0

"Cosa succede quando ottieni un lavoro come 'password' e vuoi filtrare 'ass'?" - Allora il tuo algoritmo fa schifo. –

+1

"Cosa succede quando una persona intelligente scrive 'a $$' invece - l'intento è ancora chiaro, giusto?" - Molto spesso la riduzione di un problema ha un valore, una correzione del 100% a un problema non è sempre necessaria. –

+0

@Brian - d'accordo, sto leggendo tra le righe qui. Se l'OP vuole solo creare il codice 'best effort', allora i ritocchi alla sostituzione delle stringhe vanno bene. Se lui/lei è iscritto per costruire un filtro profanità affidabile, allora lo scopo dello sforzo deve essere chiaro, o lui/lei potrebbe essere nei guai quando ci vuole un po 'più del previsto. –

2

Otterrete le migliori prestazioni elaborando uno finite state machine (FSM) (o generatene uno) e quindi analizzando il vostro input 1 carattere alla volta e camminando tra gli stati.

Puoi farlo abbastanza facilmente con una funzione che prende il tuo prossimo input char e il tuo stato attuale e che restituisce lo stato successivo, inoltre crei output mentre passi attraverso i caratteri del messaggio. Disegni l'FSM su un foglio.

In alternativa è possibile esaminare lo Windows Workflow Foundation: State Machine Workflows.

In questo modo è sufficiente camminare ogni messaggio una sola volta.

+0

A meno che non abbia frainteso il tuo suggerimento, mi sento di usare Windows Workflow State Machine su questo problema per analizzare una stringa carattere per carattere è un po 'eccessivo. –

+0

Dipende da cosa è il software. Se la persona sta cercando di costruire un software di filtraggio volgare, allora non la penso così. –

0

È possibile utilizzare Regex anziché corrispondenze di stringa semplici per evitare la sostituzione di contenuto parziale all'interno di parole. Un Regex ti consentirebbe di assicurare che stai ottenendo solo parole complete corrispondenti. Si potrebbe utilizzare un modello come questo:

"\bBADWORD\b" 

Inoltre, si consiglia di iterare il Maillist sul lato esterno, e l'elenco di parole sul ciclo interno.

1

crea un'espressione regolare dalle parole (word1|word2|word3|...) e l'utilizzo di questo, invece del ciclo esterno potrebbe essere più veloce, da allora, ogni e-mail ha solo bisogno di essere analizzato una volta. Inoltre, l'utilizzo di espressioni regolari consente di rimuovere solo "parole complete" utilizzando i marcatori di limiti di parole (\b(word1|word2|word3|...)\b).

In generale, non credo che troverete una soluzione che è ordini di grandezza più veloce di quella attuale: Si sarà dover scorrere tutte le email e si sarà avere per la ricerca di tutte le parole , non c'è un modo semplice per aggirare ciò.

1

Un algoritmo generale sarebbe quello di:

  1. generare un elenco di token in base alla stringa di input
  2. Confronta ogni token con un elenco di parole vietate
  3. (cioè trattando gli spazi separatori come token.)
  4. gettoni Sostituire abbinati

un'espressione regolare è conveniente per l'identificazione gettoni, e un HashSet fornirebbe le ricerche rapide per la vostra lista di parole vietate. Esiste un metodo Replace sovraccarico nella classe Regex che accetta una funzione, in cui è possibile controllare il comportamento di sostituzione in base alla propria ricerca.

HashSet<string> BannedWords = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) 
{ 
    "bad", 
}; 

string Input = "this is some bad text."; 

string Output = Regex.Replace(Input, @"\b\w+\b", (Match m) => BannedWords.Contains(m.Value) ? new string('x', m.Value.Length) : m.Value); 
+0

Tuttavia, questo non sfrutta la potenza di Regex. Estrae solo il ciclo di sostituzione. Vedi [risposta di Justin] (http://stackoverflow.com/questions/3864678/how-to-cut-specified-words-from-string/3864743#3864743) per quello che intendo. –

+0

@Ahmad Mago: utilizzo un'espressione regolare semplice (e veloce) per generare un flusso di token da una stringa: quale potenza ho più bisogno? Inoltre, non penso sia ideale (o performante) prendere centinaia di parole vietate e costruire una grande espressione regolare come nella soluzione di Justin. –

0

Non sarebbe più facile (e più efficiente) a loro redigere semplicemente cambiando tutti i loro personaggi per * o qualcosa del genere? In questo modo non è necessario ridimensionare o spostare alcuna stringa grande e i destinatari sono resi più consapevoli di ciò che è accaduto, piuttosto che ottenere frasi prive di senso con parole mancanti.

+0

Perché questo sarebbe più efficiente? – Heinzi

+0

@Heinzi - Modificato per includere tali informazioni. Fondamentalmente, Sostituisci dovrà spostare i dati dopo aver sostituito la stringa sostituita, a meno che ciò con cui si sta sostituendo sia esattamente lo stesso numero di caratteri. –

+0

'Sostituisci' creerà comunque un'istanza String completamente nuova, poiché le stringhe sono immutabili. Sono d'accordo con il tuo punto di usabilità, però! – Heinzi

1

Sostituirlo con * è fastidioso, ma meno fastidioso di qualcosa che rimuove il contesto della tua intenzione rimuovendo la parola e lasciando una frase malformata. Parlando della battaglia di Hastings, sarei irritato se vedessi che William aveva dato il titolo "Grand ******* of Normandy", ma almeno sapevo che stavo giocando nel campo giochi per bambini piccoli, mentre lui aveva il titolo di" Grand of Normandy " sembra un errore, o (peggio) potrei pensare che fosse in realtà il suo titolo

Non provare a sostituire le parole con altre parole innocue a meno che non sia divertente.Le persone prendono la battuta su 4chan, ma i gruppi di yahoo sulla storia avevano persone confuse perché i periodi medireview e mediareview venivano discussi quando eval (non volgarità, ma è usato in alcuni attacchi XSS a cui era stato colpito yahoo) è stato sostituito con la revisione in medievale e medievale (apparentemente, medireview è l'ortografia americana di mediareview!

+0

Questo è praticamente uguale alla mia risposta ed è stato inviato all'incirca nello stesso momento. Ogni volta che succede, la mia politica generale è che il mittente è chiaramente un genio e merita un +1. :-) –

0

Beh, certamente non voglio fare l'errore clbuttic di naive string.Replace() per farlo. La soluzione regex potrebbe funzionare, sebbene tu stia eseguendo iterazioni o utilizzando l'alternatore di tubi (e non so se/quanto possa rallentare l'operazione, in particolare per un ampio elenco di parole vietate). Potresti sempre ... non farlo, dato che è del tutto inutile, non importa cosa - ci sono modi per rendere le tue parole volute abbastanza chiare anche senza usare le lettere esatte.

Questo, ed è ridicolo avere un elenco di parole che "le persone trovano offensive" in primo luogo.C'è qualcuno che sarà offeso da praticamente qualsiasi parola

/censura è una stronzata sproloquio

1

In qualche circostanza è possibile migliorarlo: Solo per divertimento:

u possibile utilizzare SortedList, se ur mailing lista è mailing list (perché hai un delimitatore come ";") puoi fare come segue:

prima calcolare l'algoritmo del tempo di esecuzione: parole: n item. (ogni oggetto ha una lunghezza O (1)). mailing list: K item. ogni elemento della lunghezza media della mailing list di Z. ogni voce secondaria nella lunghezza media della voce della lista di posta elettronica, quindi il numero medio di elementi secondari nelle voci della mailing list è m = Z/Y.

L'algoritmo U richiede O (n * K * Z). // il modo migliore con l'algoritmo di knut

1.ora se si ordina l'elenco di parole in O (n log n).

2.1- utilizzare mailingListItem.Split (";". ToCharArray()) per ogni elemento della mailing list: O (Z). 2.2- ordina gli articoli nella mailing list: O (m * log m) l'ordinamento totale prende O (K * Z) in caso di valore rispetto a (m logm < < Z).

3- algoritmo uso merge per unire gli elementi di cattiva parola e mailing list specifica: O ((m + n) * k)

tempo totale è O ((m + n) * K + m * Z + n^2) rispetto a m < < n, il tempo di esecuzione totale dell'algoritmo è O (n^2 + Z * K) in caso di valore, che è minore di O (n * K * Z) se n < K * Z (Penso che sia così).

Quindi se le prestazioni sono molto, molto importanti, puoi farlo.

0

Presumo che si desidera rilevare solo parole complete (separate da caratteri non lettera) e ignorare le parole con una sottostringa parola filtro (come un esempio di parola p [ass]). In questo caso dovresti creare un HashSet di parole-filtro, scansionare il testo per le parole, e per ogni parola controllarne l'esistenza in HashSet. Se si tratta di una parola filtro, costruisci l'oggetto StringBuilder risultante senza di esso (o con un numero uguale di asterischi).

Problemi correlati