【问题标题】:Efficient method for checking substrings C#检查子字符串的有效方法C#
【发布时间】:2014-03-28 01:18:29
【问题描述】:

我有一堆包含 300k 行的 txt 文件。每行都有一个URL。例如。 http://www.ieee.org/conferences_events/conferences/conferencedetails/index.html?Conf_ID=30718

在一些 string[] 数组中,我有一个网站列表

amazon.com
google.com
ieee.org
...

我需要检查URL 是否包含一个网站并更新与某个网站对应的一些计数器?

目前我正在使用contains 方法,但速度很慢。数组中有大约 900 条记录,所以最坏的情况是 900*300K(对于 1 个文件)。我相信indexOf 也会很慢。

有人可以帮助我更快的方法吗?提前谢谢你

【问题讨论】:

  • 向我们展示您当前的代码。
  • 这是一个简单的并行化候选 - 您是否研究过 Parallel.For 或类似的?
  • 另外,您是否只搜索主机名?如果是这样,有一种方法可以加快速度。
  • contains 测试URI 无论如何都是一个根本性的想法。 google.com.example.com 呢?看起来你真的应该解析所有的 URL(实际上是 URI,对吗?)提取相关部分,然后在字典中查找。

标签: c# string data-structures substring


【解决方案1】:

好的解决方案会利用散列。我的方法将遵循

  1. 散列所有已知主机(您提到的string[] 集合)
  2. 将哈希存储在List<int> (hashes.Add("www.ieee.com".GetHashCode())中
  3. 对列表进行排序 (hashes.Sort())
  4. 查找网址时:
    1. 从 url 解析出主机名(从 http://www.ieee.com/... 获取 ieee.com)。您可以使用new Uri("http://www.ieee.com/...").Host 获取www.ieee.com
    2. 对其进行预处理以始终预期相同的情况。使用小写(如果你有http://www.IEee.COM/www.ieee.com
    3. 哈希解析主机名,并在hashes 列表中查找。使用BinarySearch 方法查找哈希。
    4. 如果哈希值存在,那么您的列表中有此主机

更快,内存效率更高的方法是使用Bloom filters。我建议您在 wikipedia 上阅读有关它们的信息,甚至还有布隆过滤器 on CodePlex 的 C# 实现。当然,您需要考虑到布隆过滤器允许误报结果(它可以告诉您一个值在集合中,即使它不在),因此它仅用于优化。它不会告诉您某些东西确实不在集合中。


使用Dictionary<TKey, TValue> 也是一种选择,但如果您只需要计算出现次数,那么自己维护哈希集合会更有效。

【讨论】:

  • “更有效地自己维护哈希集合。”疑。哈希冲突的可能性不为零,因此您的自定义哈希集合将需要某种方法来解决冲突。我相信你可以比 Dictionary<string, value> 做得更好,但你会花很多时间编写代码。
  • 另外,布隆过滤器并不是一个特别好的选择,因为它所做的只是告诉你一个项目是否存在。它没有计数,因此您必须在其他地方维护一个单独的计数,按主机名索引。使用 Bloom 过滤器似乎需要做很多额外的工作却没有任何收获。
  • 我不确定这一切是否正确。我没有做过实验,因此不能声称,但我真的相信由不超过 50 个字节的字母数字字符串组成的数据集哈希冲突的可能性为 0。至于保持计数,我认为将计数存储在单独的List<int> 中没有问题,其中元素数量与哈希列表相同,并且两个列表之间的对应元素索引相同。这比使用Dictionary 复杂得多,我试图提供一个关于我能想到的最有效方法的答案。
  • 只有 2^32(40 亿个和变化)32 位哈希码。仅使用字母 A-Z 就有超过 80 亿个可能的 7 个字符的字符串。所以你的第一个断言是错误的:哈希冲突的可能性很高。
  • 你是对的。谢谢你纠正我。不幸的是,这将导致实现手动冲突解决,也就是说,我同意像这样的简单壮举需要做太多的工作(除非迫切希望获得较小的性能提升)。
【解决方案2】:

创建一个Dictionary 的域来反击。

对于每个 URL,提取域(我将把这部分留给您自己弄清楚),然后在 Dictionary 中查找域并增加计数器。


我假设我们正在谈论域,因为这是您在数组中作为示例显示的内容。如果这可以是 URL 的任何部分,则将所有字符串存储在类似 trie 的结构中是可行的。

【讨论】:

    【解决方案3】:

    你可以阅读这个问题,答案会对你有所帮助:

    High performance "contains" search in list of strings in C#

    【讨论】:

    • 但他不知道完整的字符串 - 他正在尝试查找包含子字符串的字符串。
    【解决方案4】:

    在某种类似的需求中,虽然使用 indexof,但我通过一个简单的循环实现了巨大的性能提升

    类似

    int l = url.length;
    int position = 0;
    while (position < l)
    {
       if (url[i] == website[0])
       {
          //test rest of web site from position in an other loop
          if (exactMatch(url,position, website))
       }
    }
    

    似乎有点错误,但在极端情况下,在一个大型结构化 (1.2Mb) 文件中搜索一组字符串(大约 10 个)(所以正则表达式不可用),我从 3 分钟缩短到

    【讨论】:

      【解决方案5】:

      您描述的问题根本不应该涉及搜索子字符串。将您的源文件分成几行(或逐行读取),您已经知道每行都包含一个 URL,并通过一些函数运行它以提取域名,然后将其与目标域的一些快速访问记录进行比较比如Dictionary&lt;string, int&gt;,随你去递增,例如:

      var source = Enumerable.Range(0, 300000).Select(x => Guid.NewGuid().ToString()).Select(x => x.Substring(0, 4) + ".com/" + x.Substring(4, 10));
      var targets = Enumerable.Range(0, 900).Select(x => Guid.NewGuid().ToString().Substring(0, 4) + ".com").Distinct();
      var tally = targets.ToDictionary(x => x, x => 0);
      Func<string, string> naiveDomainExtractor = x=> x.Split('/')[0];
      foreach(var line in source)
      {
          var domain = naiveDomainExtractor(line);
          if(tally.ContainsKey(domain)) tally[domain]++;
      }
      

      ...在我不是特别快的机器上花费了三分之一秒,包括生成测试数据。

      诚然,您的域提取器可能更复杂一些,但它可能不会占用大量处理器,如果您有多个内核可供使用,您可以使用ConcurrentDictionary&lt;string, int&gt;Parallel.ForEach 进一步加快处理速度。

      【讨论】:

        【解决方案6】:

        您必须测试性能,但您可以尝试将网址转换为实际的 System.Uri 对象。

        将网站列表存储为 HashSet&lt;string&gt; - 然后使用 HashSet 查找 Uri 的 Host

        IEnumerable<Uri> inputUrls = File.ReadAllLines(@"c:\myFile.txt").Select(e => new Uri(e));
        string[] myUrls = new[] { "amazon.com", "google.com", "stackoverflow.com" };
        HashSet<string> urls = new HashSet<string>(myUrls);
        IEnumerable<Uri> matches = inputUrls.Where(e => urls.Contains(e.Host));
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-07-16
          • 2019-03-15
          • 1970-01-01
          • 1970-01-01
          • 2012-09-03
          • 2013-02-08
          相关资源
          最近更新 更多