2014-02-25 36 views
4

我有一堆包含300k行的txt文件。每行有一个URL。例如。 http://www.ieee.org/conferences_events/conferences/conferencedetails/index.html?Conf_ID=30718检查子字符串C的有效方法#

在一些string[]阵列我有web站点

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

我需要检查URL是否包含web站点之一,并更新对应于特定的网站一些反的名单?

现在我正在使用contains方法,但它非常慢。数组中有〜900条记录,所以最差情况是900 * 300K(对于1个文件)。我相信,indexOf也会变慢。

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

+1

向我们展示你当前的代码。 –

+0

这是一个简单的并行化候选者 - 你看过Parallel.For还是类似的? –

+0

另外,你只是要搜索主机名?如果是这样,有一种方法可以加快速度。 –

回答

3

良好的解决方案将利用散列。我的做法将是继

  1. 哈希所有已知主机(在string[]收集你提到)
  2. 存储哈希在List<int>(hashes.Add("www.ieee.com".GetHashCode()
  3. 排序列表(hashes.Sort()
  4. 当仰视一个url:
    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。我建议你在维基百科上阅读它们,甚至有一个C#实现布隆过滤器on CodePlex。当然,您需要考虑到bloom过滤器允许出现错误的肯定结果(它可以告诉您即使不是集合中的值也是如此),因此它仅用于优化。它并没有告诉你,如果它真的不是一个集合中没有的东西。


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

+0

“更有效地维护自己的哈希集合。”疑。哈希碰撞有一个非零的机会,所以您的自定义哈希集合将需要某种方式来解决冲突。我相信你可以比'Dictionary '做得更好,但是你会花费很多时间对它进行编码。 –

+0

此外,布隆过滤器并不是一个特别好的选择,因为它所做的只是告诉你是否存在物品。它不保留一个计数,因此您必须在其他地方维护一个单独的计数,并按主机名进行索引。使用布隆过滤器看起来像是一大堆额外的工作,没有收获。 –

+0

我不确定这一切是否正确。我没有做实验,因此不能声称,但我真的相信,由字母数字字符串组成的数据集的哈希冲突不超过50个字节的可能性是0。至于保持计数,我没有看到一个问题,在单独的'列表'存储计数与相同数量的元素哈希列表和相应的元素索引之间的两个列表。这比使用'Dictionary'复杂得多,我试图提供一个关于我能在短时间内想到的最有效方法的答案。 –

1

创建一个Dictionary的域来反击。

对于每个URL,提取域(我会留下那部分给你弄清楚),然后在Dictionary中查找域并增加计数器。


我认为我们在讨论域,因为这是您在数组中显示的例子。如果这可能是URL的任何部分,将所有字符串存储在类似trie的结构中可能会起作用。

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分钟,到1秒钟到<。

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>Parallel.ForEach进一步提高速度。

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));