检查子串C#的有效方法

时间:2014-02-25 16:42:23

标签: c# string data-structures substring

我有一堆包含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也会很慢。

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

6 个答案:

答案 0 :(得分:3)

好的解决方案可以利用散列。我的方法是遵循

  1. 哈希所有已知主机(您提及的string[]集合)
  2. 将哈希值存储在List<int> (hashes.Add("www.ieee.com".GetHashCode()
  3. 对列表进行排序(hashes.Sort()
  4. 查找网址时:
    1. 从网址中解析主机名(从ieee.com获取http://www.ieee.com/...)。您可以使用new Uri("http://www.ieee.com/...").Host获取www.ieee.com
    2. 预处理它总是期望相同的情况。使用小写字母(如果您http://www.IEee.COM/采取www.ieee.com
    3. 哈希解析主机名,并在hashes列表中查找。使用BinarySearch方法查找哈希值。
    4. 如果存在哈希,那么您的列表中有此主机
  5. 更快,内存效率更高的方法是使用Bloom filters。我建议你在维基百科上阅读它们,甚至还有一个C#实现的bloom过滤器on CodePlex。当然,您需要考虑布隆过滤器允许误报结果(它可以告诉您一个值在集合中,即使它不是),因此它仅用于优化。它并没有告诉你,如果真的没有,那么集合中没有东西。


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

答案 1 :(得分:1)

创建一个Dictionary域名来反击。

对于每个网址,提取域名(我会将该部分留给您查明),然后在Dictionary中查找域名并增加计数器。


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

答案 2 :(得分:0)

您可以阅读此问题,答案将对您有所帮助:

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

答案 3 :(得分:0)

在某种类似的需求中,虽然使用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分钟开始到&lt; 1秒。

答案 4 :(得分:0)

您描述的问题不应该涉及搜索子字符串。将源文件拆分为行(或逐行读取),您已知道每行包含一个URL,并通过某个函数运行它以提取域名,然后将其与目标域的某些快速访问计数进行比较例如Dictionary<string, int>,随着时间的推移递增,例如:

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<string, int>和{{ 1}}。

答案 5 :(得分:0)

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

将网站列表存储为HashSet<string> - 然后使用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));