2009-06-29 24 views
7

Usando expresiones regulares en C#, ¿hay alguna manera de encontrar y eliminar palabras o símbolos duplicados en una cadena que contenga una variedad de palabras y símbolos?Expresión regular para buscar y eliminar palabras duplicadas

Ej.

secuencia inicial de las palabras:

". Me gusta el medio ambiente El medio ambiente es bueno"

cadena deseada:

"Me gusta el ambiente es bueno"

duplicados eliminados: "el", "medio ambiente", ""

+4

¿Cuál es el propósito de esto? –

Respuesta

12

Según lo dicho por otros, se necesita más que una expresión regular para realizar un seguimiento de las palabras:

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 es preferible a ToLower, y si tiene lambdas, tiene HashSet que reemplaza Dictionary donde Key == Valor. De lo contrario, +1. – user7116

+0

Gracias. ¿Hay algún aumento en el rendimiento al usar ToUpperInvariant o es solo una convención? –

+0

El constructor HashSet toma un IEqualityComparer opcional, y su método Add devuelve un booleano que indica si el elemento ya existe en el conjunto o no. Por lo tanto, puede crear una instancia de su HashSet con "var words = new HashSet (StringComparer.OrdinalIgnoreCase);" y luego reduzca su delegado a una sola línea: "return words.Add (m.Value)? m.Value: string.Empty;" – LukeH

4

Bueno, Jeff me ha enseñado cómo usar la magia de las referencias en expresión y el modificador global para que esto ocurra, por lo que mi respuesta original no funciona. Todos deberían votar por la respuesta de Jeff. Sin embargo, para la posteridad Voy a señalar que hay un problema de sensibilidad motor de expresiones regulares poco difícil en éste, y si estuviera usando expresiones regulares con sabor a Perl, lo que tendría que hacer esto:

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

en lugar de la respuesta de Jeff, porque la expresión regular de C# capturará efectivamente \b en \1 pero PCRE no lo hará.

+1

Todos hemos pasado por este camino ... "Algunas personas, cuando se enfrentan con un problema, piensan 'Lo sé, usaré expresiones regulares'. Ahora ellos tienen dos problemas." –

+0

¿Pero hay motores de expresiones regulares hoy en día que no admiten ningún tipo de estado en estos días? Esta es una tarea bastante sencilla con referencias retrospectivas. De hecho, creo que algo así se usa para demostrar las referencias en el libro de Camel (Programming Perl). – arnsholt

+0

sí, bueno, mira mi expresión regular a continuación, que funciona –

2

Tenga una mirada en referencias hacia atrás:
http://msdn.microsoft.com/en-us/library/thwdfzxy(VS.71).aspx

Este una expresión regular que va a encontrar palabras duplicado. Pero solo coincidirá con una palabra por partido. Entonces debes usarlo más de una vez.

new Regex(@"(.*)\b(\w+)\b(.*)(\2)(.*)", RegexOptions.IgnoreCase); 

Por supuesto, esta no es la mejor solución (ver otras respuestas, que proponen no usar una expresión regular en absoluto). Pero usted pidió una expresión regular, aquí hay una. Tal vez la idea te ayude ...

0

Regex no es adecuado para todo. Algo como tu problema cae dentro de esa categoría. Te aconsejaría usar un analizador en su lugar.

-2

Como han señalado otros, esto es factible con las referencias retrospectivas. Consulte http://msdn.microsoft.com/nb-no/library/thwdfzxy(en-us).aspx para obtener detalles sobre cómo utilizar las referencias en .Net.

su problema particular para eliminar puntuacion, así lo hace un poco más complicado, pero creo que el código lo largo de estas líneas (espacio en blanco no es significativo, ya que la expresión regular) debe hacer el truco:

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

tengo No ha probado la expresión regular en absoluto, pero debe coincidir con una o más palabras separadas por espacios en blanco que se repiten. Tendrá que agregar un poco más de lógica para permitir la punción y así sucesivamente.

+0

realmente no funciona ... –

-1

usted no será capaz de utilizar expresiones regulares para este problema, ya que la expresión regular sólo coincide con los lenguajes regulares.El patrón que está intentando hacer coincidir es sensible al contexto y, por lo tanto, no es "regular".

Afortunadamente, es bastante fácil escribir un analizador. Eche un vistazo al código de Per Erik Stendahl.

1

Las expresiones regulares serían una mala elección de "herramientas" para resolver este problema. Tal vez el siguiente podría funcionar:

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: Esto me está haciendo un gran supuesto de que eres "léxico" de algún tipo de análisis como la búsqueda.

10

Esto parece funcionar para mí

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

Partidos como tal

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

¿Esto hace una coincidencia insensible a mayúsculas y minúsculas? – Robert

+0

También parece que quiere hacer coincidir la segunda instancia de la palabra, no la primera. – Robert

+0

También pruébalo en 'piratas ninjas cowboys ninjas pirates dallascowboys'. – chaos

Cuestiones relacionadas