2017-10-12 37 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進行排序,處理時不會生成數據的副本。

希望這可以幫助

相關問題