2012-11-03 38 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