我该如何在linq中将字符串与“过滤器”列表进行比较?

7

我正在尝试使用“过滤器”列表来过滤字符串集合...这个列表包含了一些不良单词。如果字符串中包含了列表里的一个单词,我就不想要它。

我已经做到了一部分,这里的不良单词是"frakk":

string[] filter = { "bad", "words", "frakk" };

string[] foo = 
{ 
    "this is a lol string that is allowed", 
    "this is another lol frakk string that is not allowed!"
};

var items = from item in foo 
            where (item.IndexOf( (from f in filter select f).ToString() ) == 0)
            select item;

但这并没有起作用,为什么?
3个回答

9
您可以使用包含 + 任何
var items = foo.Where(s => !filter.Any(w => s.Contains(w)));

如果您想进行不区分大小写的比较:

var items = foo.Where(s => !filter.Any(w => s.IndexOf(w, StringComparison.OrdinalIgnoreCase) >= 0));
更新: 如果您想排除至少有一个单词在筛选列表中的句子,您可以使用String.Split()Enumerable.Intersect:
var items = foo.Where(sentence => !sentence.Split().Intersect(filter).Any());

Enumerable.Intersect非常高效,因为它在内部使用了Set。将长序列放在前面会更加高效。由于Linq的延迟执行机制,在第一次匹配成功后会停止执行。

(请注意,“空”的Split也包括其他空白字符,如制表符或换行符)


1
这个解决方案不会捕获部分单词吗?例如,它将标记“badmitten”为不良单词。 - JaredPar
@JaredPar:编辑了我的答案,加入了“方法”一词。 - Tim Schmelter

2
你需要解决的第一个问题是将句子分解成一系列单词。最简单的方法是根据空格进行分割。
string[] words = sentence.Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries);

从那里,您可以使用简单的LINQ表达式查找粗俗语言

var badWords = words.Where(x => filter.Contains(x));

然而,这只是一个比较简单的解决方案。它无法处理许多复杂情况,你需要考虑。

  • 有许多字符可以作为空格。我的解决方案只使用了' '
  • 分割无法处理标点符号。所以dog!将不会被视为dog。最好根据合法字符拆分单词

将不良词汇列表连接起来,并将其作为正则表达式匹配放入,这样做比担心所有单词分割的各种排列组合更有意义吗? - Jim Wooley
@JimWooley,这可能是解决问题的更好方法。特别是因为Regex已经具有用于单词边界检测(\b)的内置机制。但在推荐正则表达式之前,我仍然想了解更多情况,我将它们保留给非关键场景(适用于快速JavaScript验证,但不适用于服务器上的任何内容)。 - JaredPar
为什么服务器端正则表达式不好?(真正的问题) - Mark Walsh
1
@MarkWalsh,即使是经常使用它们的人也很容易搞砸。有太多情况需要考虑,很难证明你做得完全正确。例如,大多数正则表达式不考虑Unicode字符串,因此很容易被它们破坏。 - JaredPar
啊,好的,我明白你的观点,特别是关于编码的问题。在这方面,我也遇到了一些不好的结果,很遗憾它们经常是无法避免的。 - Mark Walsh

0
你的初始尝试失败的原因是这行代码:
(from f in filter select f).ToString()

评估为由linq表达式部分隐含的Array Iterator类型名称的字符串。因此,您实际上正在比较以下字符串的字符:

System.Linq.Enumerable+WhereSelectArrayIterator``2[System.String,System.String]

在检查短语时,要看过滤器的实际效果而非其字面意思。


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