带有位置的大型排序集合的C#数据类型?

时间:2020-03-05 18:57:17  来源:igfitidea点击:

我正在尝试比较SQL查询中的两个大型数据集。现在,SQL查询是在外部完成的,每个数据集的结果都保存到了自己的csv文件中。我的小型Cconsole应用程序加载了两个text / csv文件,并比较它们之间的差异并将差异保存到文本文件中。

它是一个非常简单的应用程序,它仅将第一个文件中的所有数据加载到arraylist中,并在从第二个csv文件读取每一行时对arraylist执行.compare()。然后保存不匹配的记录。

该应用程序可以运行,但是我想提高性能。我认为,如果我可以利用两个文件都经过排序的事实,则可以极大地提高性能,但是我不知道C中的数据类型可以保持顺序并允许我选择特定位置。有一个基本的数组,但我不知道每个列表中将有多少个项目。我可能有超过一百万条记录。我应该查看可用的数据类型吗?

解决方案

回答

好吧,有几种方法可行。我们可以编写自己的数据结构来执行此操作。或者,我们可以尝试使用SortedList。我们也可以用代码返回DataSet,然后在表上使用.Select()。当然,我们必须在两个表上都执行此操作。

回答

我们可以轻松地使用SortedList进行快速查找。如果我们要加载的数据已经排序,则插入SortedList的速度不应太慢。

回答

如果我们只是想查看FileA中的所有行是否都包含在FileB中,则可以读入它,并仅在循环内比较流。

文件1
条目1
参赛作品2
参赛作品3

文件2
条目1
参赛作品3

我们可以遍历两个计数器并找到遗漏,逐行遍历每个文件,看看我们是否得到了所需的东西。

回答

也许我误会了,但是ArrayList会按照添加元素的顺序来维护其元素。这意味着我们仅需根据比较结果增加两个扫描索引,就可以一次通过比较两个ArrayList。

回答

我有一个问题是我们是否考虑过"外包"比较。我们可以调用很多优秀的差异工具。如果没有一个文件可以让我们指定两个文件并仅获取差异,我会感到惊讶。只是一个想法。

回答

System.Collections.Specialized.StringCollection允许我们添加值的范围,并使用.IndexOf(string)方法允许我们检索该项目的索引。

话虽这么说,我们可能只是从文件流中加载了几个byte []并进行了字节比较……甚至不用担心将这些内容加载到诸如StringCollection或者string []这样的正式数据结构中;如果我们要做的只是检查差异,并且想要速度,那么我会破坏字节的差异。

回答

如果两个CSV文件中的数据已经排序并且记录数相同,则可以完全跳过数据结构并进行就地分析。

StreamReader one = new StreamReader("C:\file1.csv");
StreamReader two = new StreamReader("C:\file2.csv");
String lineOne;
String lineTwo;

StreamWriter differences = new StreamWriter("Output.csv");
while (!one.EndOfStream)
{
    lineOne = one.ReadLine();
    lineTwo = two.ReadLine();
    // do your comparison.
    bool areDifferent = true;

    if (areDifferent)
        differences.WriteLine(lineOne + lineTwo);
}

one.Close();
two.Close();
differences.Close();

回答

我认为每个人都有这么多不同答案的原因是,我们对问题的说明不够明确,无法回答。首先,这取决于我们要跟踪哪种差异。我们是否希望像WinDiff中那样输出差异,其中第一个文件是"原始"文件,第二个文件是"已修改"文件,以便我们可以将更改列出为INSERT,UPDATE或者DELETE?我们是否有一个主键,可以让我们将两行匹配为同一记录的不同版本(当除主键以外的其他字段不同时)?还是这只是某种和解,我们只想让差异输出说出"在文件1中记录而在文件2中未记录"之类的内容?

我认为这些问题的解答大家为问题提供合适的答案。

回答

如果我们有两个文件(如帖子中所述),每个文件分别为一百万行,则可能会占用大量内存。一些性能问题可能是我们正在从磁盘交换。如果我们只是将文件A的第1行与文件B的第1行,行2的文件A->行2的文件B等进行比较,我建议我们使用一种不会在内存中存储太多的技术。我们可以阅读先前评论者发布的两个文件流的注销,并在找到它们时"实时"写出结果。这不会在内存中显式存储任何内容。我们还可以将每个文件的大块转储到内存中(一次说一千行)到类似列表的内容中。可以微调以满足需求。

回答

这是对David Sokol的代码的一种改编,可以处理不同数量的行,并输出一个文件中的行,而不输出另一个文件中的行:

StreamReader one = new StreamReader("C:\file1.csv");
StreamReader two = new StreamReader("C:\file2.csv");
String lineOne;
String lineTwo;
StreamWriter differences = new StreamWriter("Output.csv");
lineOne = one.ReadLine();
lineTwo = two.ReadLine();
while (!one.EndOfStream || !two.EndOfStream)
{
  if(lineOne == lineTwo)
  {
    // lines match, read next line from each and continue
    lineOne = one.ReadLine();
    lineTwo = two.ReadLine();
    continue;
  }
  if(two.EndOfStream || lineOne < lineTwo)
  {
    differences.WriteLine(lineOne);
    lineOne = one.ReadLine();
  }
  if(one.EndOfStream || lineTwo < lineOne)
  {
    differences.WriteLine(lineTwo);
    lineTwo = two.ReadLine();
  }
}

有关写在我头顶上的代码的标准警告适用于我们-我们可能需要特殊情况下,其中一行用尽而另一行仍然用尽,但是我认为这种基本方法应该可以满足需求。

回答

为了解决问题1,我建议我们考虑创建每行的哈希值。这样,我们可以使用字典快速轻松地比较哈希值。

要解决问题2,一个快速而肮脏的解决方案是使用IDictionary。使用itemId作为第一个字符串类型,并将行的其余部分用作第二个字符串类型。然后,我们可以快速查找是否存在itemId并比较行。当然,这假定.Net 2.0+