检查一个字符串是否包含来自一个字符串列表中的元素

230

以下是一段代码块:

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

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

14个回答

502

使用C#和LINQ(我现在不太了解VB):

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

或者(更短更高效,但可能不太清晰):

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

如果你正在测试相等性,那么值得考虑使用HashSet等数据结构,但是这对于部分匹配并没有帮助,除非你将其拆分成片段并增加复杂度顺序。

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

更新:在最新的.NET版本中,Contains具有可选的StringComparison参数,可以用于不区分大小写的比较,例如:myString.Contains(s,StringComparison.CurrentCultureIgnoreCase);


1
根据他的示例,我会使用StartsWith而不是Contains。 - tvanfosson
@tvanfosson - 这取决于示例是否完全包含,但是我同意。当然可以很容易地更改。 - Marc Gravell
@ICR - 对于包含匹配,你很难做出大量的改进。如果是“以...开始”,我已经添加了一个关于二分查找的注释 - 那样会更快。 - Marc Gravell
后一种比lambda更有效率的原因是什么? - ca9163d9
1
我发现将这个方法放在一个带有 params 关键字的扩展方法中,使得它更易读。例如:myString.ContainsAny("this", "that", "those"); - Connell
显示剩余6条评论

16

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

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

6

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

这是解决方案:

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

难道不应该是 >-1 吗? - CSharped
2
@CSharped 不重要,因为“>-1”(大于负一)和“>=0”(大于或等于零)是同一件事。 - WhoIsRich

5
之前有一个类似的问题 "Best way to test for existing string against a large list of comparables",提出了一些建议。
对于您的需求,正则表达式可能已经足够。表达式将是所有候选子字符串的连接,并在它们之间使用 OR "|" 运算符。当构建表达式时,您需要注意未转义的字符,或者由于复杂性或大小限制而无法编译表达式。
另一种方法是构建一个 trie 数据结构 来表示所有候选子字符串(这可能会有些重复正则表达式匹配器正在做的事情)。当您遍历测试字符串中的每个字符时,您将创建一个指向 trie 根节点的新指针,并将现有指针推进到适当的子节点(如果有)。当任何指针到达叶子节点时,就会得到匹配结果。

3
myList.Any(myString.Contains);

3

虽然这是一个旧问题,但由于原始要求是使用VB.NET,因此可以使用已接受答案的相同值:

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

3
作为我需要检查一个列表中的项是否出现在一个(很长的)字符串中,我最终得到了这个函数:
listOfStrings.Any(x => myString.ToUpper().Contains(x.ToUpper()));

或者在vb.net中:

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

2

根据您的模式,一个改进方法是改用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。


重新开始使用starts-with;也许可以预先排序并使用二分查找?这可能会更快。 - Marc Gravell

2
< p > Contains 方法的缺点是不允许指定比较类型,而在比较字符串时,这经常很重要。它始终是区分文化和大小写敏感的。因此,我认为 WhoIsRich 的答案很有价值,我只想展示一个更简单的替代方案:

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

1

你测试过速度了吗?

例如,你创建了一个数据样本并进行了分析吗?它可能没有你想象的那么糟糕。

这也可能是你可以将其分离到单独的线程中并给予速度幻觉的东西!


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