Как вырезать указанные слова из строки - PullRequest
5 голосов
/ 05 октября 2010

Существует список запрещенных слов (или строк, чтобы быть более общим) и другой список, скажем, по почте пользователей.Я хотел бы удалить все запрещенные слова из всех писем.

Тривиальный пример:

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

Как я могу улучшить этот алгоритм?


Спасибо за советы.Я проголосовал за несколько ответов, но не отметил ни одного как ответ, так как это было больше похоже на обсуждение, чем на решение.Некоторые люди пропустили запрещенные слова с плохими словами.В моем случае мне не нужно беспокоиться о том, чтобы распознать «sh1t» или что-то в этом роде.

Ответы [ 12 ]

5 голосов
/ 05 октября 2010

Простые подходы к фильтрации ненормативной лексики не будут работать - сложные подходы, по большей части, тоже не работают.

Что происходит, когда вы получаете работу типа «пароль» и хотите отфильтровать «задницу»? Что происходит, когда какой-то умный человек пишет вместо этого «$$» - намерение все еще ясно, верно?

См. Как реализовать хороший фильтр ненормативной лексики? для подробного обсуждения.

2 голосов
/ 05 октября 2010

Вы можете использовать RegEx, чтобы сделать вещи немного чище:

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

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

Даже это, однако, далеко от совершенства, так как люди всегда найдут способ обойти фильтр любого типа.

2 голосов
/ 05 октября 2010

Вы получите наилучшую производительность, составив конечный автомат (FSM) (или сгенерировав его), а затем проанализировав введенный 1 символ за раз и пройдя через состояния.

Вы можете сделать это довольно легко с помощью функции, которая принимает ваш следующий символ ввода и ваше текущее состояние и возвращает следующее состояние, вы также создаете вывод, проходя символы почтового сообщения.Вы рисуете FSM на бумаге.

В качестве альтернативы вы можете заглянуть в Windows Workflow Foundation: рабочие процессы конечного автомата .

Таким образом, вам нужно только пройти каждое сообщениеодин раз.

1 голос
/ 05 октября 2010

При некоторых обстоятельствах возможно улучшить его: Просто для удовольствия:

вы можете использовать SortedList, если ваш список рассылки является списком рассылки (потому что у вас есть разделитель типа ";"), вы можете сделать следующее:

Первый алгоритм расчета времени работы: Слова: п. (каждый элемент имеет длину O (1)). список рассылки: K шт. каждый элемент в списке рассылки средней длины Z. средняя длина каждого подпункта в элементе списка рассылки Y, поэтому среднее количество подэлементов в элементах списка рассылки равно m = Z / Y.

Ваш алгоритм принимает O (n * K * Z). // лучший способ с алгоритмом кнута

1. Теперь, если вы сортируете список слов в O (n log n).

2.1- используйте mailingListItem.Split (";". ToCharArray ()) для каждого элемента списка рассылки: O (Z). 2.2 - отсортировать элементы в списке рассылки: O (m * log m) полная сортировка занимает O (K * Z) в стоимостном случае по отношению к (m logm << Z). </p>

3 - использовать алгоритм слияния для объединения элементов с плохим словом и определенного списка рассылки: O ((m + n) * k)

общее время равно O ((m + n) * K + m * Z + n ^ 2) по отношению к m << n, общее время выполнения алгоритма равно O (n ^ 2 + Z * K) , который меньше, чем O (n * K * Z), если n <K * Z (я так думаю). </p>

Так что, если производительность очень, очень, очень важна, вы можете сделать это.

1 голос
/ 05 октября 2010

Замена его на * раздражает, но менее раздражает, чем то, что удаляет контекст вашего намерения, удаляя слово и оставляя искаженное предложение. Обсуждая Битву при Гастингсе, я был бы раздражен, если бы увидел, что Уильяму дали титул "Grand ******* of Normandy"", но, по крайней мере, я бы знал, что я играю на детской площадке для маленьких детей, в то время как его титул "Великий «Нормандия» просто выглядит как ошибка, или (что еще хуже) я могу подумать, что на самом деле это был его титул.

Не пытайтесь заменить слова более безобидными словами, если это не смешно. Люди получают шутку о 4chan, но группы Yahoo об истории сбивали с толку людей, потому что периоды medireview и mediareview обсуждались, когда eval (не ненормативную лексику, но используется в некоторых атаках XSS, которые были поражены Yahoo) был заменен обзором в средневековье. и средневековый (по-видимому, medireview - это американское написание медиа-обозрения!).

1 голос
/ 05 октября 2010

Общий алгоритм будет таким:

  1. Создание списка токенов на основе входной строки (т. Е. Путем обработки пробелов в качестве разделителей токенов)
  2. Сравните каждый токен со списком запрещенных слов
  3. Заменить соответствующие токены

Регулярное выражение удобно для идентификации токенов, а HashSet обеспечит быстрый поиск вашего списка запрещенных слов. В классе Regex есть перегруженный метод Replace, который принимает функцию, в которой вы можете управлять поведением замены на основе вашего поиска.

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);
1 голос
/ 05 октября 2010

Создание регулярного выражения из слов (word1|word2|word3|...) и использование его вместо внешнего цикла может быть быстрее, поскольку с тех пор каждое электронное письмо необходимо анализировать только один раз. Кроме того, использование регулярных выражений позволит удалить только «полные слова» с помощью маркеров границы слова (\b(word1|word2|word3|...)\b).

В общем, я не думаю, что вы найдете решение, которое на несколько порядков быстрее вашего текущего: вам придется перебирать все письма, а будет нужно искать все слова, простого пути нет.

0 голосов
/ 06 октября 2010

Я получил отличные результаты, используя этот алгоритм на codeproject.com лучше, чем замены текста методом грубой силы.

0 голосов
/ 05 октября 2010

Я предполагаю, что вы хотите обнаружить только полные слова (разделенные не буквенными символами) и игнорировать слова с подстрокой слова фильтра (как пример слова p [ass]). В этом случае вы должны создать себе HashSet из слов-фильтров, отсканировать текст на наличие слов и для каждого слова проверить его наличие в HashSet. Если это слово фильтра, тогда создайте получившийся объект StringBuilder без него (или с равным количеством звездочек).

0 голосов
/ 05 октября 2010

Ну, вы, конечно же, не хотите совершать грубую ошибку наивной строки. Замените (), чтобы сделать это.Решение для регулярных выражений может работать, хотя вы либо используете итерации, либо используете генератор каналов (и я не знаю, если / насколько это замедлит вашу работу, особенно для большого списка запрещенных слов).Вы всегда можете просто ... не делать этого, поскольку это совершенно бесполезно, несмотря ни на что - есть способы сделать ваши слова совершенно ясными, даже не используя точные буквы.

Это, и смешно иметьсписок слов, которые «люди находят оскорбительными» в первую очередь.Есть кто-то, кто будет обижен почти любым словом

/ цензура - чушь дурака

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...