2012-11-03 41 views
2

我有一個日誌文件,每行有單個字符串。我試圖從文件中刪除重複的數據並將文件保存爲新文件。我首先想到了將數據讀入HashSet,然後將哈希集的內容保存出來,但是當嘗試執行此操作時(在將該字符串添加到哈希集的行上),我得到一個「OutOfMemory」異常。從大文件中刪除冗餘數據

文件中有大約32,000,000行。每次比較重新讀取整個文件是不現實的。

任何想法?我的另一個想法是將整個內容輸出到一個SQLite數據庫並選擇DISTINCT值,但我不確定它能與那麼多值一起工作。

感謝您的任何意見!

+0

您可以用'File.ReadAllLines'沒有一個OutOfMemoryException?我假設你在32位系統上運行它。 –

+0

我在64位系統上運行它,並且我還沒有嘗試過ReadAllLines。我確實想知道有多少項目被淘汰,所以我正在使用一個StreamReader讀取每一行並將其添加到哈希集。我正在測試.Add()方法的結果以查看它是否返回false來計算冗餘項目的數量。如果可能,我想保留此功能。 – rune711

回答

2

您需要考慮的第一件事 - 高內存消耗是一個問題嗎?

如果您的應用程序將始終在服務器上可用大量的RAM運行,或者你知道你有足夠的內存任何其他情況下,你可以做很多的事情,如果你的應用程序將運行你不能做在低內存環境中或在未知環境中。如果內存不是問題,那麼請確保您的應用程序是作爲64位應用程序運行的(當然,在64位操作系統上),否則您將被限制爲2GB內存(4GB,如果您使用LARGEADDRESSAWARE旗)。我想在這種情況下,這是你的問題,你所要做的就是改變它 - 它會很好(假設你有足夠的內存)。如果內存是問題,而且你不需要使用太多的內存,你可以按照你的建議將所有的數據添加到數據庫中(我更熟悉像SQL Server這樣的數據庫,但我猜SQLite會這麼做) ,確保在列上有正確的索引,然後選擇不同的值。

另一種選擇是將文件作爲流逐行讀取,爲每行計算散列值,並將行保存到其他文件中,並將散列保留在內存中。如果哈希已經存在,然後移動到下一行(並且,如果您願意,添加到刪除了多行的計數器)。在這種情況下,你可以在內存中保存更少的數據(只有散列沒有重複的項目)。

祝你好運。

+0

我重新編譯成64bit。這讓我有足夠的記憶空間。文件流/哈希也是一個好主意!謝謝! – rune711

2

您是否嘗試過使用數組並初始化的HashSet。我假設HashSet的加倍算法是OutOfMemoryException的原因。

var uniqueLines = new HashSet<string>(File.ReadAllLines(@"C:\Temp\BigFile.log")); 

編輯

我測試了。新增()方法的結果,看它是否 返回false來算那是多餘的項目數。我想 喜歡保持此功能,如果可能的話。

那麼你應該嘗試,用來初始化的HashSet與文件行的正確(最大)尺寸:

int lineCount = File.ReadLines(path).Count(); 
List<string> fooList = new List<String>(lineCount); 
var uniqueLines = new HashSet<string>(fooList); 
fooList.Clear(); 
foreach (var line in File.ReadLines(path)) 
    uniqueLines.Add(line); 
+0

請看上面我的評論。我希望能夠知道有多少物品被刪除,我認爲使用此方法不會允許這樣做。 – rune711

+0

@ rune711:編輯我的答案,以提供一種不同的方式來節省內存。 –

1

我使用HashSet對Tim進行了類似的處理。我確實增加了手動計數和比較。

我從我的windows 8安裝中讀取安裝日誌,它的大小爲58MB,大小爲312248行,並在.993秒內在LinqPad中運行。

var temp=new List<string>(10000); 
var uniqueHash=new HashSet<int>(); 
int lineCount=0; 
int uniqueLineCount=0; 

using(var fs=new FileStream(@"C:\windows\panther\setupact.log",FileMode.Open,FileAccess.Read)) 
    using(var sr=new StreamReader(fs,true)){ 
     while(!sr.EndOfStream){ 
     lineCount++; 
     var line=sr.ReadLine(); 
     var key=line.GetHashCode(); 
      if(!uniqueHash.Contains(key)){ 
       uniqueHash.Add(key); 
       temp.Add(line); 
       uniqueLineCount++; 
        if(temp.Count()>10000){ 
         File.AppendAllLines(@"c:\temp\output.txt",temp); 
         temp.Clear(); 
        } 
      } 
     } 
    } 
Console.WriteLine("Total Lines:"+lineCount.ToString()); 
Console.WriteLine("Lines Removed:"+ (lineCount-uniqueLineCount).ToString()); 

perf in linqpad