【问题标题】:C# Compare large text files and write differenceC#比较大文本文件并写入差异
【发布时间】:2016-09-05 16:39:30
【问题描述】:

简化问题:我有两个不能使用字符串数组存储在内存中的大文件,也不能使用 except 方法,因为它也会将文件数据推送到内存中。我的目标是比较两个大文件并找出差异,但我无法通过将其中一个大文件加载到内存中来做到这一点。有没有好的流式阅读器逐行解决方案或其他方法来做到这一点?

长问题: 我有两个文件 1.SQL查询数据 2. 物理数据

SQLQueryData 包含数据库中列出的文件的名称(例如:Recording01.wmvAudiofile01.wmaTestrecording.wmv 等)

PhysicalData 是物理 HDD 上的目录搜索,以获取那里存在的文件名(这应该包含与 SQLQueryData 相同的信息)。

我特意从他们的目录中删除了该测试的文件,并创建了数百万个测试名称。 SQLQueryData 文件为 700MB,PhysicalData 约为 650MB。

我首先尝试了string[] readfile = file.readalllines,但这会导致内存不足错误。

我也对这两个文件尝试了IEnumerable<String>,但这会导致

系统内存不足错误

因为它将两个文件都放入内存中,这很容易占用大约 2GB 的内存。

我的下一个尝试是只将一个文件加载到内存中,然后使用流读取器将单行 SQLQueryData 与 PhysicalData 的字符串数组进行比较,但这也会导致内存不足错误。

我尝试将流读取器嵌入到另一个流读取器中,但我在网上看到的唯一方法或示例是 IF 语句,说明流读取器的 line1 = line2 是否写入该数据。我不想写line1=line2,我需要知道PhysicalData是否不包含SQLQueryData信息。

我正在考虑使用一种提取方法,例如如果line1=line2,则从文件中提取该行,但我不知道如何在代码中编写。

有人知道按照我的要求去做吗?

【问题讨论】:

  • 您能否将文件逐行加载到 SQL 数据库中。并使用 SQL 进行比较?
  • 区分不是一项微不足道的任务。您是否考虑过缺少的行以及如何以及何时同步?
  • 文件是否已排序?您可以将查询输出文件替换为与数据库的物理连接吗?您是否有能力向数据库添加索引,或者是否有适当的索引?能否实现合并排序,以便对文件进行排序?
  • 不幸的是,这些文件没有排序。我正在制作一个我必须做的项目的真实示例。我们有一个包含多个文件名的数据库,这些文件名不按顺序排列,并且在 HDD 上缺少数据文件。我的目标是找出丢失的文件并专门报告这些文件。不幸的是,由于这是现实世界,我们正在谈论数百万个文件。

标签: c# compare ienumerable


【解决方案1】:

查看 StreamReader 的 ReadLine 方法:

https://msdn.microsoft.com/en-us/library/system.io.streamreader.readline(v=vs.110).aspx

另请参阅此处的一些教程,特别注意那里的“使用”(比使用 try/finally 和显式 Dispose 更易读):

http://www.dotnetperls.com/streamreader

【讨论】:

  • 谢谢,但我已经阅读了这些。就像我的帖子所述,我使用流式阅读器开始读取这些行,但其他线程中提供的示例正在写入彼此相等的行。目前,我正在以提取的心态来解决这个问题,但我不确定如何以 line1=line2 然后从文件中删除 line2 的方式对其进行编码。我也不确定如何让它说如果它读取完整的文件长度并且没有找到匹配项,则写入该特定行。任何一种方法都应该有效,但我找不到解决方案
  • 显然你必须吐出第三个文件来“删除”一些东西。为了检查下一部分是否存在一行,您必须多次重新解析文件。除非您想使用更多的临时存储(在内存和/或磁盘中),例如从原始文件构建八叉树或一些索引,然后使用它并在完成后删除临时内容
【解决方案2】:

几点:

  1. 为了使这个性能接近可接受,您需要首先对内容进行排序以启用二进制搜索。您可以通过制定一个算法来实现这一点,该算法从前到后读取文件,如果它们无序则交换任意两个连续的行,重复直到排序。或者你可以使用它:http://www.codeproject.com/Articles/285123/Sorting-Huge-Text-Files

    只需对其中一个文件进行排序。

  2. 如果文件的格式不是固定宽度(每行相同的字节数),那么您将需要创建一个索引来告诉您每行的起始字节偏移量。如果可能,索引应保存在内存中(如果您希望文件大小 > 4GB,则 List 或 long[] 是理想的选择)

  3. 为了获得最佳性能,我建议使用 MemoryMappedFile:https://msdn.microsoft.com/en-us/library/system.io.memorymappedfiles.memorymappedfile(v=vs.110).aspx 来访问您正在阅读的两个文件。视图至少应该是最大线的大小。

  4. 一旦您对其中一个文件进行了排序(如果不是固定宽度,则对两个文件都进行了索引),开始循环遍历未排序文件的 long[],读取该字节偏移处的行,然后使用二进制搜索在另一个文件中找到相应的条目。如果没有找到,请将差异写入第三个文件,即您的差异文件。

【讨论】:

  • 这可能有效。我现在就试试。一组数字将小于一组文件名(它们使用 GUID,因此文件名类似于 012093-12341-xasd-recordingname.wmv)
  • 如果你走这条路,就不需要做任何二进制搜索,因此不需要行偏移索引。您可以对这两个文件进行排序,然后运行合并操作以查找匹配项和不匹配项。这种方法应该更快更简单。
  • @glenebob 你很有帮助。我使用了类似上面的 SQL 方法,但花了一个多小时才完成。我需要找到一些方法来简化这一点,因为现实世界的文件可能高达 2-4GB。 SQL 方法需要很长时间,如果它能够完成的话。我曾考虑将文件大小分解为 100MB 的文件并制作多个文件而不是一个大文件,但我仍然不知道如何与所有文件进行比较,因为现在数据被拆分到多个位置
  • 您的表是否已编入索引?并非必须如此,SQL 服务器可以使用与上述相同的方法 - 对两个集合进行排序和合并,这应该比索引查找更快。你用的是什么关系型数据库?
  • @glenebob 是的,我在创建表后立即对其进行了索引。仍然花了大约一个小时。我尝试创建一个“主键”索引,但由于表已满而失败,我还尝试使用标志位(0 表示音频,1 表示视频)以帮助它更快地搜索,但绝对数量文件填充了 tempdb。
【解决方案3】:

要按顺序比较行,文件必须已经排序。要对大文件进行排序,您可以使用http://classCentric.com/SortHugeFile。下面的 sn-p 是 http://classCentric.com/SortedDiff 工具的一部分,它处理边缘情况,支持正则表达式提取,并允许数值比较。 SortedDiff.cs 将 testFile 与 masterFile 进行比较,并输出前 3 行中提到的文件。完整的源代码可下载。

TextWriter twTest = new StreamWriter("inTestFile.txt");
TextWriter twMaster = new StreamWriter("inMasterFile.txt");
TextWriter twInBoth = new StreamWriter("inBothFiles.txt");
using (StreamReader tr = new StreamReader(testFile))
{
    using (StreamReader mr = new StreamReader(masterFile))
    {
        mLine = mr.ReadLine();

        while ((tLine = tr.ReadLine()) != null)
        {
            if (mLine != null) break;
            //-1: tLine < mLine; 1: tLine > mLine
            comp = String.Compare(tPart, mPart, oStringComparison);

            //while value in test file < that in master, increment test's pointer to process next tLine
            if (comp < 0)
            {
                twTest.WriteLine(tLine);
                continue;
            }

            //while value in test file > that in master, increment master's pointer to process next mLine
            while (comp > 0)
            {
                twMaster.WriteLine(mLine);

                mLine = mr.ReadLine();
                if (mLine == null) break;

                comp = String.Compare(tPart, mPart, oStringComparison);
            }

            if (comp == 0)
            {
                twInBoth.WriteLine(mLine);
                mLine = mr.ReadLine();
            }
            else //comp must be < 0 since "copm > 0" is handled in while & "== 0" after that
            {
                twTest.WriteLine(tLine);
            }
        }//while
    }//using
}//using

【讨论】:

    猜你喜欢
    • 2014-06-08
    • 1970-01-01
    • 2016-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多