C# 需要在非常大的文件中的每列中查找不同值的数量

C# 需要在非常大的文件中的每列中查找不同值的数量,c#,memory-management,large-files,distinct-values,C#,Memory Management,Large Files,Distinct Values,我正在用C#处理大文件(希望如此),我需要一种方法来确定文件每列中不同值的数量。我已经阅读了所有关于用C#确定不同值的问题。挑战在于,由于某些文件的大小很大,而且一列中可能有数千万个不同的值(可能有数百列--各种数据类型),因此要创建列表、字典或数组,等等。对于每一列,然后使用前面回答的问题中描述的技术,将使我面临达到2GB内存限制的危险 目前,我一次读取/处理一行文件,并对每一行“清理和消毒”数据,更新聚合结果,然后将处理后的每一行写入输出文件,然后将输出文件批量插入SQL。迄今为止的表现实际

我正在用C#处理大文件(希望如此),我需要一种方法来确定文件每列中不同值的数量。我已经阅读了所有关于用C#确定不同值的问题。挑战在于,由于某些文件的大小很大,而且一列中可能有数千万个不同的值(可能有数百列--各种数据类型),因此要创建列表、字典或数组,等等。对于每一列,然后使用前面回答的问题中描述的技术,将使我面临达到2GB内存限制的危险

目前,我一次读取/处理一行文件,并对每一行“清理和消毒”数据,更新聚合结果,然后将处理后的每一行写入输出文件,然后将输出文件批量插入SQL。迄今为止的表现实际上相当不错

由于数据最终登录到MS SQL中,作为一种后备方法,我可以使用SQL来确定不同的值,但理想情况下,我希望在登录到SQL之前能够做到这一点。如有任何想法或建议,我们将不胜感激

更新:我为每个字段创建了一个哈希表,并为每个字段添加了新的不同值。在处理结束时,我使用 myDistinctValues.Count 以获取计数。这对于小文件很好,但正如我担心的那样,对于大文件,我会

System.OutOfMemoryException 
扔。根据建议,我确实尝试添加了

<runtime>
    <gcAllowVeryLargeObjects enabled="true"/>
</runtime>


到我的应用程序配置,但这没有帮助。

您希望有多少不同的值?我使用了以下简单的应用程序:

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);
            }
        }

    }
}
使用系统;
使用System.Collections.Generic;
班级计划
{
静态void Main(字符串[]参数)
{
字典ds=新字典;
随机r=新随机();
对于(int i=0;i<100000000;i++){
字符串s=Guid.NewGuid().ToString();
d[s]=r.Next(0,1000000);
如果(i%100000==0)
{
Console.Out.WriteLine(“Dict size:+d.Count”);
}
}
}
}
与.net 4.6.1、x64 build target一起,在我的计算机内存耗尽之前,我已经消耗了4000万个唯一对象和5.5 GB的内存(对不起,目前正在处理其他事情)

如果要使用阵列,可能需要一个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>

您应该能够计算出跟踪不同值及其计数所需的内存类型。我建议你一次只写一个专栏,如果你认为它会有几亿美元的话

还有一点小小的澄清:当我读到“不同值的数量”时,我觉得你想跟踪每个值出现的次数。这就是为什么我使用了
字典
——字符串是要计数的不同值,int是计数


如果你想把一个X百万或十亿个值的列表分成不同的列表,而不需要对出现的次数进行计数,那么hash集可能是更轻的权重

< p>你是否考虑得到一个值的哈希代码(假设它不能大于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);
}
static void Main(字符串[]args)
{
List VAL=新列表{1,'c',as',2,1};
foreach(var v以VAL为单位)
Console.WriteLine($“Is uniques:{IsUniq(v)}”);
Console.ReadKey();
}
私有静态哈希集_hashes=new HashSet();
专用静态布尔IsUniq(对象v)
{
返回_hashes.Add(v);
}

100万个元素的原始数据应该是100-150兆字节。

虽然我的解决方案并不优雅,而且肯定有更好的解决方案(BTree?),但我发现了一些行之有效的方法,并想与大家分享。我不可能是唯一一个想要确定超大文件中字段的不同计数的人。也就是说,我不知道这将如何扩展到数亿或数十亿条记录。在某些情况下,如果数据足够,单个阵列的大小将达到2GB的限制

什么不起作用:

  • 对于非常大的文件:在迭代文件时,为每个字段实时填充哈希表,然后使用hashtable.count。哈希表的总大小在到达文件末尾之前会导致SystemOutOfMemoryException
  • 将数据导入SQL,然后对每列使用SQL来确定不同的计数。时间太长了
什么有效:

  • 对于具有数千万行的大型文件,我首先对前1000行进行分析,其中我为每个字段创建一个哈希表,并用不同的值填充
  • 对于1000个值中有50个以上不同值的任何字段,我用布尔标志HasHighDensityOfDistinctValues=true标记该字段
  • 对于HasHighDensityOfDistinctValues==true的任何此类字段,我创建一个单独的文本文件,并在遍历主文件时,将该字段的值写入特定于字段的文本文件
  • 对于不同值密度较低的字段,我维护每个字段的哈希表,并将不同值写入其中
  • 我注意到,在许多高密度字段中,多个连续行都会出现重复值(例如PersonID),因此,为了减少字段特定文本文件的条目数,我存储字段的上一个值,并且仅在当前值不等于上一个值时才写入文本文件。这大大减少了字段特定文本文件的总大小
  • 遍历正在处理的主文件后,我将遍历FieldProcessingResults类,对于每个字段,如果HashHighDensityOfDistincTvalues==true,我将读取字段特定文本文件中的每一行,并填充字段特定哈希
    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