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


155

对于以下代码块:

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)进行检查。

有没有更好(更有效)的方法来编写此代码?

Answers:


359

使用LINQ并使用C#(这些天我对VB不太了解):

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

或(更简短,更有效,但可以说不够清晰):

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

如果您正在测试相等性,那值得一看,HashSet等等,但这对部分匹配没有帮助,除非您将其拆分成多个片段并增加了复杂性的顺序。


更新:如果您的意思是“ StartsWith”,则可以对列表进行排序并将其放入数组中;然后使用Array.BinarySearch查找每个项目-通过查找检查它是完全匹配还是部分匹配。


1
根据他的示例,我将使用StartsWith而不是Contains。
tvanfosson,2009年

@tvanfosson-取决于示例是否完全包含在内,但是可以,我同意。当然,更改很简单。
马克·格雷韦尔

在算法级别上,此代码在多大程度上更有效?如果“ Any”中的循环更快,它将更短且更快,但是您必须多次执行精确匹配的问题是相同的。
Torsten Marek

如果使用集合,则可以设置自定义比较器。
Fortyrunner,2009年

通过实践中的任何可测量的差异,第二种方法实际上并没有更高的效率。
ICR

7

当您构建自己的字符串时,应该像这样

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

5

先前有一个类似的问题“ 针对大量可比对象测试现有字符串的最佳方法 ”提出了许多建议。

正则表达式可能足以满足您的要求。该表达式将是所有候​​选子字符串的串联,并且|在它们之间使用OR“ ”运算符。当然,在构建表达式时,您必须注意未转义的字符,否则由于复杂性或大小限制而无法编译它。

这样做的另一种方法是构造一个trie数据结构来表示所有候选子字符串(这可能在某种程度上重复了regex匹配器的操作)。当您逐步遍历测试字符串中的每个字符时,将创建一个指向trie根的新指针,并将现有的指针前进到适当的子代(如果有)。当任何指针到达叶子时,您将获得一个匹配项。


5

我喜欢Marc的答案,但需要包含匹配项为CaSe InSenSiTiVe。

这是解决方案:

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

应该不是> -1吗?
CSharped

1
@CSharped无关紧要,因为> -1(大于负1)和> = 0(大于或等于零)是同一回事。
WhoIsRich

2

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

另外,根据您的模式,您似乎可以提取myString路径的第一部分,然后反向进行比较-在字符串列表中查找myString的起始路径,而不是相反。

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

return listOfStrings.Contains( startPath );

编辑:使用HashSet想法@Marc Gravell提到,这将更快,因为您可以更改ContainsContainsKey,并且查找将是O(1)而不是O(N)。您必须确保路径完全匹配。请注意,这不是@Marc Gravell的通用解决方案,而是针对您的示例量身定制的。

对不起,C#示例。我没有足够的咖啡来翻译成VB。


重新开始- 也许预先排序并使用二进制搜索?那可能会更快。
Marc Gravell


1

你测试过速度了吗?

即您是否创建了样本数据集并对其进行了概要分析?它可能没有您想象的那么糟糕。

这也可能是您可以生成单独的线程并产生速度幻觉的东西!


0

如果速度至关重要,则可能需要为模式集寻找Aho-Corasick算法

这是一个带有失败链接的特例,即复杂度为O(n + m + k),其中n是输入文本的长度,m是模式的累积长度,k是匹配项的数量。您只需要修改算法即可在找到第一个匹配项后终止。



0

Contains方法的缺点是不允许指定比较类型,而比较类型在比较字符串时通常很重要。它始终对文化和大小写敏感。所以我认为WhoIsRich的答案很有价值,我只想显示一个更简单的选择:

listOfStrings.Any(s => s.Equals(myString, StringComparison.OrdinalIgnoreCase))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.