从单词列表中替换字符串中的多个单词

7

我有一个单词列表:

string[] BAD_WORDS = { "xxx", "o2o" } // 实际上我的列表有很多,大约100个单词

而我有一些文本(通常很短,最多250个单词),我需要从中删除所有的 BAD_WORDS

我已经尝试了这个方法:

    foreach (var word in BAD_WORDS)
    {
        string w = string.Format(" {0} ", word);
        if (input.Contains(w))
        {
            while (input.Contains(w))
            {
                input = input.Replace(w, " ");
            }
        }
    }

但是,如果文本以不良词语开头或结尾,则不会被删除。 我使用了空格,因此它不会匹配部分单词,例如"oxxx"不应该被删除,因为它不是与不良词汇的完全匹配。

有人可以就此向我提供建议吗?


1
看起来需要用正则表达式来解决。 - Oded
你为什么要包含这行代码 string w = string.Format(" {0} ", word); - Nikhil Agrawal
你的问题是什么,你的代码看起来很好?只需删除if并使用startswith和endswith即可。 - Peter
@Nikhil Agrawal:在单词前后加上空格。如果只保留单词,它也会匹配例如 oxxx。 - Tudor
1
你的 if 是不必要的。最好从 while 开始,以避免第一次检查两次。 - Tim S.
7个回答

18
string cleaned = Regex.Replace(input, "\\b" + string.Join("\\b|\\b",BAD_WORDS) + "\\b", "")

等一下,我错过了什么...正在工作中...好了,修好了。 :) - shannon
嘿... :) 谢谢Dementic。照我说的做,不要照我做的。我只是想说所有的嵌套、LINQ 和循环都有一个简单的老式/经过实践验证的方法。 - shannon
如果替换需要多次进行,生成的正则表达式可以缓存以供重复使用。我建议使用Regex.Escape,以防BAD_WORDS中包含对正则表达式语法有重大意义的内容。 - Jon Hanna
也许代码不是完美的,正如其他人指出需要改进的地方,但是使用正则表达式单词边界而不是分割,值得点赞。 - Tim S.

6

这是一个很适合使用Linq和Split方法的任务。尝试以下代码:

return string.Join(" ", input.Split(' ').Where(w => !BAD_WORDS.Contains(w)));

只要空格足够,这个方法就可以匹配到单词。但是如果单词在开头或结尾,或者后面跟着换行符、标点符号等,则无法匹配。如果需要处理这种情况,使用基于正则表达式的方法会更好。 - Jon Hanna
这会在单词之间添加额外的空格,我不知道为什么。 - Professor Zoom
空字符串正在与其他项两侧的空格连接。我已编辑答案(现在更整洁!) - James Ellis-Jones

1
在字符串变量input的前后添加虚假空格,这样它就能检测到第一个和最后一个单词。
input = " " + input + " ";

 foreach (var word in BAD_WORDS)
    {
        string w = string.Format(" {0} ", word);
        if (input.Contains(w))
        {
            while (input.Contains(w))
            {
                input = input.Replace(w, " ");
            }
        }
    }

然后把字符串截取:

input = input.Trim();

那是个好主意,那会修复我的代码,但是难道没有更好的解决方案吗?这段代码对我来说有点奇怪,我写它只是因为没有其他想法。 - Rafael Herscovici

1
您可以将文本中的单词存储到一个列表中。然后只需检查所有单词是否在不良列表中,类似于这样:
List<string> myWords = input.Split(' ').ToList();
List<string> badWords = GetBadWords();

myWords.RemoveAll(word => badWords.Contains(word));
string Result = string.Join(" ", myWords);

1
您可以使用StartWith和EndsWith方法,例如:
while (input.Contains(w) || input.StartsWith(w) || input.EndsWith(w) || input.IndexOf(w) > 0)
{
   input = input.Replace(w, " ");
}

希望这能解决你的问题。

你是不是想说“或”而不是“且”?根据你的测试,它必须同时开始、结束和包含这个单词。 - Tudor
这将仍然捕获部分单词(badword ='aoooo',实际单词='aoooome',它将删除'aoooo')。 - Rafael Herscovici

0

我想强调一下,你应该在for循环内部使用while来完成:

   foreach (var word in BAD_WORDS)
{
    while (input.Contains(String.Format(" {0} ", word);))
    {
        input = input.Replace(w, " ");
    }
}

不需要那个if和'w'变量,在任何情况下,我都会使用上面Antonio Bakula的答案,这是我想到的第一件事。


你正在尝试替换你已经从代码中删除的 w。 没有 w,它将替换部分单词匹配。 - Rafael Herscovici

0
根据以下帖子,最快的方法是使用正则表达式和MatchEvaluator函数来替换字符串中的多个字符: 在字符串中替换多个字符,最快的方法是什么?
        Regex reg = new Regex(@"(o2o|xxx)");
        MatchEvaluator eval = match =>
        {
            switch (match.Value)
            {
                case "o2o": return " ";
                case "xxx": return " ";
                default: throw new Exception("Unexpected match!");
            }
        };
        input = reg.Replace(input, eval);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接