2009-06-29 14 views
7

Utilizzando le espressioni regolari in C#, esiste un modo per trovare e rimuovere parole o simboli duplicati in una stringa contenente una varietà di parole e simboli?Espressione regolare per trovare e rimuovere parole duplicate

Es.

stringa iniziale di parole:

". Mi piace l'ambiente L'ambiente è buono"

stringa desiderata:

"Mi piace l'ambiente è buono"

duplicati rimossi: "il", "ambiente", ""

+4

Qual è lo scopo? –

risposta

12

Come detto da altri, è necessario più di una regex per tenere traccia di parole:

var words = new HashSet<string>(); 
string text = "I like the environment. The environment is good."; 
text = Regex.Replace(text, "\\w+", m => 
        words.Add(m.Value.ToUpperInvariant()) 
         ? m.Value 
         : String.Empty); 
+0

ToUpperInvariant è preferito a ToLower, e se hai lambda, hai HashSet che sostituisce Dictionary dove Key == Valore. Altrimenti, +1. – user7116

+0

Grazie. Ci sono miglioramenti nelle prestazioni dall'uso di ToUpperInvariant o è solo una convenzione? –

+0

Il costruttore HashSet accetta un IEqualityComparer opzionale e il suo metodo Add restituisce un valore booleano che indica se l'elemento esiste già nel set. Quindi puoi istanziare il tuo HashSet con "var words = new HashSet (StringComparer.OrdinalIgnoreCase);" e quindi riduci il tuo delegato a un one-liner: "return words.Add (m.Value)? m.Value: string.Empty;" – LukeH

4

Bene, Jeff mi ha mostrato come utilizzare la magia delle backreferenze in-expression e il modificatore globale per far sì che questo accada, quindi la mia risposta originale non è operativa. Dovresti votare tutti per la risposta di Jeff. Tuttavia, per i posteri ti faccio notare che c'è un piccolo problema di sensibilità motore regex difficile in questo, e se si stesse usando regex Perl al gusto, si avrebbe bisogno di fare questo:

\b(\S+)\b(?=.*\b\1\b.*) 

invece di risposta di Jeff, poiché la regex di C# acquisirà effettivamente \b in \1 ma PCRE no.

+1

Siamo stati tutti giù per questa strada ... "Alcune persone, di fronte a un problema, pensano:" Lo so, userò le espressioni regolari ". Ora hanno due problemi. " –

+0

Ma ci sono dei motori regex oggi che non supportano alcun tipo di stato al giorno d'oggi? Questo è un compito piuttosto semplice con backreferences. In effetti, penso che qualcosa del genere sia usato per dimostrare le retroconti nel libro Camel (Programming Perl). – arnsholt

+0

sì, beh, vedi la mia regex sotto la quale funziona –

2

Dai un'occhiata alla backreference:
http://msdn.microsoft.com/en-us/library/thwdfzxy(VS.71).aspx

Questa una regex che troverà le parole raddoppiati. Ma corrisponderà solo una parola per partita. Quindi devi usarlo più di una volta.

Ovviamente questa non è la soluzione migliore (vedere altre risposte, che propongono di non utilizzare una regex affatto). Ma hai chiesto una regex - eccone uno. Forse solo l'idea ti aiuta ...

0

Regex non è adatto a tutto. Qualcosa come il tuo problema rientra in quella categoria. Ti consiglierei di usare un parser invece.

-2

Come altri hanno sottolineato, questo è fattibile con backreferences. Vedi http://msdn.microsoft.com/nb-no/library/thwdfzxy(en-us).aspx per i dettagli su come utilizzare i riferimenti remoti in .Net.

Il tuo problema particolare per rimuovere la punteggiatura e lo rende un po 'più complicato, ma credo che il codice in questo senso (gli spazi non è significativo in quanto regex) dovrebbe fare il trucco:

(\b\w+(?:\s+\w+)*)\s+\1 

ho non è stata testata la regex, ma dovrebbe corrispondere a una o più parole separate da spazi bianchi che vengono ripetute. Dovrai aggiungere un po 'di logica per consentire la punteggiatura e così via.

+0

non funziona davvero ... –

-1

Non sarà in grado di utilizzare le espressioni regolari per questo problema, in quanto espressione regolare corrisponde solo linguaggi regolari.Lo schema a cui stai cercando di corrispondere è sensibile al contesto e, quindi, non "regolare".

Fortunatamente, è abbastanza facile scrivere un parser. Dai un'occhiata al codice di Per Erik Stendahl.

1

Le espressioni regolari sarebbero una scelta scadente di "strumenti" per risolvere questo problema. Forse il seguente potrebbe funzionare:

HashSet<string> corpus = new HashSet<string>(); 
char[] split = new char[] { ' ', '\t', '\r', '\n', '.', ';', ',', ':', ... }; 

foreach (string line in inputLines) 
{ 
    string[] parts = line.Split(split, StringSplitOptions.RemoveEmptyEntries); 
    foreach (string part in parts) 
    { 
     corpus.Add(part.ToUpperInvariant()); 
    } 
} 

// 'corpus' now contains all of the unique tokens 

EDIT: Questo sono io fare un grande presupposto che sei "lexing" per una sorta di analisi come la ricerca.

10

Questo sembra funzionare per me

(\b\S+\b)(?=.*\1) 

Partite in questo modo

 
apple apple orange 
orange red bluegreen orange green blue 
piratesninjas cowboys ninjas pirates 
+0

Si esegue una corrispondenza senza distinzione tra maiuscole e minuscole? – Robert

+0

Inoltre sembra che voglia abbinare la seconda istanza della parola, non la prima. – Robert

+0

Prova anche su "pirati ninjas cowboys ninja pirates dallascowboys'. – chaos

Problemi correlati