2017-10-12 36 views
0

我正在C#中处理大型文件(希望),并且需要一种方法来确定文件每列中不同值的数量。我已经阅读了所有我能找到的有关使用C#确定不同值的问题。面临的挑战是,由于某些文件的大小和一列(可能有数百列 - 各种数据类型)中数千万个不同值的可能性,因此需要创建列表,字典或数组等等。每一列 - 然后使用先前回答的问题中描述的技术 - 会使我处于达到2 GB内存限制的危险中。需要在非常大的文件中的每列中查找不同值的数量

目前,我正在读取/处理文件一行一行,并对每行“清理和清理”数据,更新聚合结果,然后将每个处理的行写入输出文件,然后将其批量插入到SQL 。到目前为止的表现实际上相当不错。

由于数据最终落在MS SQL中,作为后备我可以使用SQL来确定不同的值,但我希望能够在登陆SQL之前完成此操作。任何想法或建议表示赞赏。

更新:对于每个字段,我创建了一个哈希表并为每个字段添加了新的不同值。在处理结束时,我使用 myDistinctValues.Count 获取计数。这工作正常的小文件,但正如我担心的,我得到一个大文件

System.OutOfMemoryException 

抛出。根据一个建议,我曾尝试添加到我的应用程序配置

<runtime> 
    <gcAllowVeryLargeObjects enabled="true"/> 
</runtime> 

但这并没有帮助。

+0

Wirh这个严格的要求只有某种持久性BTree浮现在脑海。 – bamanow

+0

什么2GB内存限制?如果为AnyCPU体系结构编译并在x64机器上运行,则不应该有这样的限制。 –

+1

@KevinAnderson,我指的是:https://blogs.msdn.microsoft.com/joshwil/2005/08/10/bigarrayt-getting-around-the-2gb-array-size-limit/ – AndrewBanjo1968

回答

1

尽管我的解决方案不够优雅,但确实有更好的解决方案(BTree?),但我发现了一些工作并认为我会分享它。我不可能是唯一一个在那里寻找非常大的文件中的字段确定不同的计数。也就是说,我不知道这将如何扩展到数亿甚至数十亿的记录。在某些情况下,如果有足够的数据,则会达到单个阵列的2GB大小限制。

什么不工作:

  • 对于非常大的文件:哈希表用于实时填充,因为我通过文件迭代每个字段,然后使用hashtable.count。哈希表的集合大小在到达文件末尾之前导致SystemOutOfMemoryException。
  • 将数据导入到SQL,然后在每列上使用SQL来确定不同的计数。需要WAY时间太长。

什么做的工作:

  • 对于拥有数百万行的我第一次在我创建一个哈希表中的每个领域的第1000行进行分析,并与填充大文件不同的价值观。
  • 对于超过1000个值超过50个不同值的字段,我用布尔标志HasHighDensityOfDistinctValues = true标记该字段。
  • 对于HasHighDensityOfDistinctValues == true的任何这样的字段,我创建了一个单独的文本文件,并且在迭代主文件时,我只将该字段的值写入字段特定的文本文件。
  • 对于不同值的密度较低的字段,我维护每个字段的哈希表并为其写入不同的值。
  • 我注意到在许多高密度字段中,对于多个连续行存在重复值(例如PersonID),因此为了减少字段特定文本文件的条目数量,我存储了以前的值如果当前值不等于先前的值,则只写入文本文件。这大大减少了字段特定文本文件的总大小。
  • 一旦迭代完成正在处理的主文件,我遍历我的FieldProcessingResults类和每个字段,如果HasHighDensityOfDistinctValues == true,我读取特定于字段的文本文件中的每一行,并用字段特定的哈希表填充不同的值,然后使用HashTable.Count来确定不同值的计数。
  • 在转到下一个字段之前,我存储与该字段关联的计数,然后使用myHashTable.Clear()清除哈希表。在移动下一个字段之前,我关闭并删除字段特定的文本文件。

以这种方式,我能够获得每个字段的不同值的计数,而不必为每个字段同时填充和维护内存中的哈希表,这会导致内存不足错误。

0

您期待多少个不同的值?我用下面简单的应用程序:

using System; 
using System.Collections.Generic; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Dictionary<string, int> ds = new Dictionary<string, int>; 
     Random r = new Random(); 
     for (int i = 0; i < 100000000; i++) { 
      string s = Guid.NewGuid().ToString(); 

      d[s] = r.Next(0, 1000000); 

      if (i % 100000 == 0) 
      { 
       Console.Out.WriteLine("Dict size: " + d.Count); 
      } 
     } 

    } 
} 
与.NET 4.6.1

在一起,64位构建目标我有4000万个唯一对象之前,我跑出我的机器内存消耗的存储5.5千兆字节(它的忙与此刻,抱歉)其他东西..

如果你打算使用数组,你可能东东,看起来像一个app.config:

<?xml version="1.0" encoding="utf-8"?> 
<configuration> 
    <startup> 
     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/> 
    </startup> 
    <runtime> 
     <gcAllowVeryLargeObjects enabled="true"/> 
    </runtime> 
</configuration> 

你应该能够制定出你需要什么样的记忆来跟踪不同的值和他们的计数。我建议你一次只能在一列上工作,如果你认为它将在数亿美元中。

只是一个小的澄清:当我读到“不同值的数量”,这让我觉得你想要跟踪每个值出现的次数。这就是为什么我使用Dictionary<string, int> - 字符串是被计数的不同值并且int是计数

如果您想要将X百万/十亿个值的清单重新分配到不同的值,需要计算的出现则HashSet的可能是重量更轻

+0

谢谢你。我不需要对每个值的实例进行计数,只需要计算每个字段的总体独立计数。我对你建议的内存管理技术很感兴趣,我会探讨这一点。谢谢! – AndrewBanjo1968

0

你认为得到一个值的散列码(假设它不能超过128个字节的),创建一个哈希集合,做这样的事情:

static void Main(string[] args) 
{ 
    List<object> vals = new List<object> {1, 'c', "as", 2, 1}; 

    foreach(var v in vals) 
     Console.WriteLine($"Is uniques: {IsUniq(v)}"); 

    Console.ReadKey(); 
} 

private static HashSet<object> _hashes = new HashSet<object>(); 
private static bool IsUniq(object v) 
{ 
    return _hashes.Add(v); 
} 

它应该像100-150兆字节的100万个元素的原始数据。

0

您是否尝试过将文件加载到数据表中,然后通过dataview(而不是创建副本)进行独特的选择? 退房

https://social.msdn.microsoft.com/Forums/vstudio/en-US/fccda8dc-4515-4133-9022-2cb6bafa8ad9/how-does-a-dataview-act-in-memory?forum=netfxbcl

下面是一些伪代码

Read from File into Datatable 
Create DataView with sort on the column you want 
UniqueCount = 0 
var CurrentValue="<some impossible value>" 
For each ViewRow in DataView 
    If CurrentValue <> ViewRow["MyColumn"] 
     UniqueCount ++ 

UniqueCount should give me my result 

,因为你只使用2个变量UniqueCount和CurrentValue的通过数据循环这将是有效的。 您也正在对dataview进行排序,处理时不会生成数据的副本。

希望这可以帮助

相关问题