【问题标题】:Check if a string contains an element from a list (of strings)检查字符串是否包含(字符串)列表中的元素
【发布时间】:2010-10-04 19:05:25
【问题描述】:

对于以下代码块:

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

有没有更好(更高效)的方式来编写这段代码?

【问题讨论】:

    标签: c# vb.net list coding-style performance


    【解决方案1】:

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

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

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

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

    如果您正在测试相等性,则值得查看 HashSet 等,但这对部分匹配没有帮助,除非您将其拆分为片段并添加复杂性顺序。


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

    【讨论】:

    • 根据他的例子,我会使用 StartsWith 而不是包含。
    • @tvanfosson - 这取决于这些示例是否完全包含在内,但是是的,我同意。当然,很容易改变。
    • 这段代码在算法层面的效率有多高?如果“Any”中的循环更快,则更短更快,但是您必须执行多次精确匹配的问题是相同的。
    • 如果您使用的是集合,您可以设置自定义比较器。
    • 第二个在实践中没有任何可衡量的差异。
    【解决方案2】:

    之前的类似问题“Best way to test for existing string against a large list of comparables”提供了许多建议。

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

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

    【讨论】:

      【解决方案3】:

      根据您的模式,一项改进是改为使用 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。

      【讨论】:

      • 重新开始;也许预先排序并使用二进制搜索?这可能会再次更快。
      【解决方案4】:

      如果速度很关键,您可能需要查找 Aho-Corasick algorithm 以获取模式集。

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

      【讨论】:

        【解决方案5】:

        你测试过速度吗?

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

        这也可能是你可以在一个单独的线程中产生的东西,并给人一种速度的错觉!

        【讨论】:

          【解决方案6】:
          myList.Any(myString.Contains);
          

          【讨论】:

            【解决方案7】:

            我喜欢 Marc 的回答,但需要将 Contains 匹配为 CaSe InSensiTiVe。

            这是解决方案:

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

            【讨论】:

            • 不应该是 >-1 吗?
            • @CSharped 没关系,因为 >-1(大于负 1)和 >=0(大于或等于零)是一回事。
            【解决方案8】:

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

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

            【讨论】:

              【解决方案9】:

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

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

              【讨论】:

                【解决方案10】:

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

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

                【讨论】:

                  【解决方案11】:

                  因为我需要检查(长)字符串中是否有列表中的项目,所以我最终得到了这个:

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

                  或者在 vb.net 中:

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

                  【讨论】:

                    【解决方案12】:

                    略有不同,我需要查找字符串中是否存在整个单词且不区分大小写。

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

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

                    【讨论】:

                      猜你喜欢
                      • 2017-11-20
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2020-02-26
                      • 1970-01-01
                      • 2019-04-11
                      • 2013-09-17
                      • 1970-01-01
                      相关资源
                      最近更新 更多