2012-04-11 18 views
5

Necesito comprobar si una cadena contiene malas palabras.C# - La forma más rápida de encontrar un conjunto de cadenas en otra cadena

Siguiendo un consejo de otra pregunta aquí, he hecho un HashSet que contiene las palabras:

HashSet<string> swearWords = new HashSet<string>() { "word_one", "word_two", "etc" }; 

Ahora necesito para ver si alguno de los valores contenidos en swearWords son en mi cadena.

que he visto hacer a la inversa, por ejemplo:

swearWords.Contains(myString) 

Pero esto devolverá falso.

¿Cuál es la forma más rápida de comprobar si alguna de las palabras en el HashSet está en myString?

Nota: me imagino que puedo usar un bucle foreach para revisar cada palabra, y romper si encuentro una coincidencia, solo me pregunto si hay una manera más rápida.

+0

¿Por qué estás usando un 'HashSet'? Puede ser más fácil de usar 'List ' aquí. Y luego divida 'myString' en una lista y haga la comparación necesaria. – SkonJeet

+1

@SkonJeet: si la lista de palabrotas es grande, la verificación de la contención será más rápida para un 'HashSet' que una' List', y no puedo ver que 'List' lo haga * más fácil *. –

+0

Originalmente estaba usando una lista y luego la convertí a HashSet cuando leí que son más rápidos para verificar valores en – surfitscrollit

Respuesta

6

Usted podría tratar de una expresión regular, pero no estoy seguro de que es Más rápido.

Regex rx = new Regex("(" + string.Join("|", swearWords) + ")"); 
rx.IsMatch(myString) 
+2

+1 - Jurar las palabras se describen mejor como expresiones regulares. Estoy hablando de mi experiencia. Sin embargo, es prácticamente imposible vencer a los usuarios con un algoritmo estático y una lista de palabras. –

9

Si se colocan las palabrotas en un IEnumerable <> contenedor de aplicación:

var containsSwears = swarWords.Any(w => myString.Contains(w)); 

Nota: HashSet <> implementa IEnumerable <>

+2

'HashSet ' implementa 'IEnumerable '. (Y debe tener cuidado con el problema de Scunthorpe si usa este enfoque: http://en.wikipedia.org/wiki/Scunthorpe_problem) – LukeH

+0

@LukeH: buen punto, pero más allá del alcance de esta discusión. Tal vez mejor como un comentario sobre la pregunta. +1 – Sprague

+0

lol @ scunthorpe, buen nombre. Sin embargo, si su lógica para romper las palabras funciona, debe estar libre de ese problema porque está revisando palabras enteras, no cadenas dentro de palabras. Un problema que podría tener es asociar mayúsculas y minúsculas a las palabras o palabras que usan leet speak. –

3

El principal problema con este tipo de esquemas es definir qué es una palabra en el contexto de la cadena que desea comprobar .

  • Implementaciones ingenuas como las que usan input.Contains simplemente no tienen el concepto de una palabra; ellos "detectarán" malas palabras incluso cuando esa no era la intención.
  • Romper palabras en espacios en blanco no va a reducirlo (considere también los signos de puntuación, etc.).
  • Romper en caracteres que no sean espacios en blanco va a plantear problemas de cultura: ¿qué personajes se consideran caracteres de palabras exactamente?

Suponiendo que su lista de palabras prohibidas solo utiliza el alfabeto latino, una opción práctica sería suponer que las palabras son secuencias que constan solo de caracteres latinos.Así una solución de partida razonable sería

var words = Regex.Split(@"[^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Pc}\p{Lm}]", myString); 

la expresión regular anterior es la clase estándar \W modificado para no incluir dígitos; para más información, vea http://msdn.microsoft.com/en-us/library/20bw873z.aspx. Para otros enfoques, vea this question y posiblemente el enlace CodeProject incluido en la respuesta aceptada.

haya dividido la cadena de entrada, se puede iterar sobre words y reemplazar aquellos que coincidan con cualquier cosa en su lista (use swearWords.Contains(word) para comprobar) o simplemente detectar si hay coincidencias en absoluto con

var anySwearWords = words.Intersect(swearWords).Any(); 
Cuestiones relacionadas