检查字符串是否包含字符串列表中的元素

对于下面的代码块:

For I = 0 To listOfStrings.Count - 1
If myString.Contains(lstOfStrings.Item(I)) Then
Return True
End If
Next
Return False

输出结果为:

案例1:

myString: C:\Files\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: True

案例2:

myString: C:\Files3\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: False

列表(listOfStrings)可能包含几个项目(至少20个),它必须检查成千上万的字符串(如myString)。

是否有更好(更有效)的方法来编写这段代码?

332681 次浏览

使用LINQ,使用c#(我现在不太懂VB):

bool b = listOfStrings.Any(s=>myString.Contains(s));

或者(更短更有效,但可以说不太清楚):

bool b = listOfStrings.Any(myString.Contains);

如果你要测试是否相等,那么值得看看HashSet等,但这对部分匹配没有帮助,除非你把它分成片段并添加一个复杂的顺序。


更新:如果你真的是指“StartsWith”,那么你可以对列表进行排序并将其放入数组;然后使用Array.BinarySearch来找到每一项——通过查找来检查它是完全匹配还是部分匹配。

从之前类似的问题“最好的方法来测试现有的字符串对一个大的可比较的列表”中有许多建议。

Regex可能足以满足您的需求。表达式将是所有候选子字符串的连接,它们之间有一个OR "|"操作符。当然,在构建表达式时,您必须注意未转义的字符,或者由于复杂性或大小限制而编译失败。

另一种方法是构造Trie数据结构来表示所有候选子字符串(这可能在某种程度上重复正则表达式匹配器所做的工作)。当您逐步遍历测试字符串中的每个字符时,您将创建一个指向树根的新指针,并将现有指针推进到适当的子节点(如果有的话)。当任何指针到达叶节点时,就会得到匹配。

根据您的模式,一个改进是使用StartsWith而不是Contains。StartsWith只需要遍历每个字符串,直到找到第一个不匹配的字符,而不必在找到一个字符时在每个字符位置重新开始搜索。

同样,根据您的模式,看起来您可能能够提取myString路径的第一部分,然后反向比较——在字符串列表中查找myString的起始路径,而不是相反。

string[] pathComponents = myString.Split( Path.DirectorySeparatorChar );
string startPath = pathComponents[0] + Path.DirectorySeparatorChar;


return listOfStrings.Contains( startPath );

编辑:使用@Marc Gravell提到的HashSet思想会更快,因为你可以将Contains更改为ContainsKey,查找将是O(1)而不是O(N)。你必须确保路径完全匹配。请注意,这不是一个通用的解决方案是@Marc Gravell的,但适合你的例子。

很抱歉是c#的例子。我还没喝够咖啡,没办法翻译成VB。

如果速度是关键,你可能想要寻找Aho-Corasick算法的模式集。

它是一个带有失败链接的单词查找树,也就是说,复杂度是O(n+m+k),其中n是输入文本的长度,m是模式的累积长度,k是匹配的数量。您只需修改算法,以便在找到第一个匹配后终止。

你测试过速度了吗?

例如,你是否创建了一组样本数据并对其进行了分析?可能没有你想的那么糟。

这也可能是你可以衍生到一个单独的线程,给速度的错觉!

myList.Any(myString.Contains);

我喜欢Marc的回答,但需要Contains匹配不区分大小写。

这就是解决方案:

bool b = listOfStrings.Any(s => myString.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0))

当你构造你的字符串时,它应该是这样的

bool inact = new string[] { "SUSPENDARE", "DIZOLVARE" }.Any(s=>stare.Contains(s));

Contains方法的缺点是它不允许指定比较类型,这在比较字符串时通常很重要。它总是区分区域性和大小写的。所以我认为WhoIsRich的答案是有价值的,我只是想展示一个更简单的选择:

listOfStrings.Any(s => s.Equals(myString, StringComparison.OrdinalIgnoreCase))

老问题。但是由于VB.NET是最初的需求。使用已接受答案的相同值:

listOfStrings.Any(Function(s) myString.Contains(s))

当我需要检查一个(长)字符串列表中是否有项目时,我最终得到了这个:

listOfStrings.Any(x => myString.ToUpper().Contains(x.ToUpper()));

或者在vb.net中:

listOfStrings.Any(Function(x) myString.ToUpper().Contains(x.ToUpper()))

轻微的变化,我需要找到是否有完整的单词和大小写不敏感的字符串。

myString.Split(' ', StringSplitOptions.RemoveEmptyEntries).Intersect(listOfStrings).Any())

对于不区分大小写的myStringlistOfStrings已转换为大写。