Profanity Filter using a Regular Expression (list of 100 words)

29.7k views Asked by At

What is the correct way to strip profane words from a string given:
1) I have a list of 100 words to look for in an array of strings. 2) What is the correct way to handle partial words? How do most people handle this? For example the word mass. Then sometimes a partial word is also bad - assume foobar is an extremely profane word I may want to disallow foobar and foobar* and *foobar.

So do you put all the words into a single expression or loop through the list?

What's the right way to tackle it? I'm using Groovy/Grails but any modern languages examples welcome.

3

There are 3 answers

0
Hisoka On

I was working on this regex eariler, and it should match spaces, and other characters in between aswell:

/^f(\s|.{1,2})?o?(\s|.{1,2})?o.*$/gi

Validates:

foo

f.o.o

f oo

foobar

0
voidmain On

This is quite a difficult problem to solve and you need determine if regular expressions will work for you and how you handle embedding (when you add a dictionary word to profanity like frackface except with the real F-word).

Regular expressions generally have a limit to how long they can be and this usually prevents you from using a single regex for all your words. Executing multiple regular expressions against a string is really slow, depending on what performance you need and how big your blacklist gets. We initially implement CleanSpeak as a regular expression system, but it didn't scale and we rewrote it using a different mechanism.

You also need to consider phrases, punctuation, spaces, leet-speak and other languages. All of these make regular expressions less appealing as a solution. Here are some examples using the word hello (assume it is profanity for this exercise):

  • List item
  • h e l l o
  • h.e.l.l.o
  • h_e_l_l_o
  • |-|ello
  • h3llo
  • "hello there" (this phrase might not contain any profane words but combined they are profane)

You also need to handle edge cases where two or more dictionary (whitelist) words contain a profanity when next to each other. Some examples that contain the s-word:

  • bash it
  • ssh it's quiet time

These are obviously not profanity, but most homegrown and many commercial solutions have problems with these cases.

We have spent the last 3 years perfecting the filter used by CleanSpeak to ensure it handles all of these cases and we continue to tweak it and make it better. We also spent 8 months perfecting our system for performance and it can handle about 5,000 messages per second. Not to say you can't build something usable, but be prepared to handle a lot of issues that might come up and also to create a system that doesn't use regular expressions.

0
kelloti On
  1. Concatenate each word into a list of words - (foobar|foobaz|...)
  2. Then put guards on either side of the grouping for extraneous characters

    [^!@#$%^&*]*(foobar|foobaz|foofii)[^!@#$%^&*]*

Also, you'll probably want to use a case insensitive flag so that it'll also match words like FooBaz and fOObaR.

As far as performance goes, concatenating this as one big regex is probably fastest (although I'm not an expert). The regex algorithm is pretty efficient at searching & handling branch conditions. Basically, it must be better than O(mn) (where m is the number of words and n is the size of the text you're searching)