【问题标题】:Parsing a big CSV file C# .net 4解析一个大的 CSV 文件 C# .net 4
【发布时间】:2012-07-13 01:05:54
【问题描述】:

我知道以前有人问过这个问题,但我似乎无法用我读过的答案来解决这个问题。我有一个 CSV 文件 ~ 1.2GB,如果我像 32 位一样运行进程,我会得到 outOfMemoryException,如果我将它作为 64 位进程运行,它可以工作,但它仍然需要 3.4GB 的内存,我知道我在我的 customData 类中存储了很多数据,但仍然有 3.4gb 的内存?,我在读取文件时做错了吗? dict 是一本字典,其中我只有一个映射到要保存某些内容的属性,具体取决于它所在的列。我的阅读方式是否正确?

StreamReader reader = new StreamReader(File.OpenRead(path));
while(!reader.EndOfStream)  {
            String line = reader.ReadLine();
            String[] values = line.Split(';');
            CustomData data = new CustomData();
            string value;
            for (int i = 0; i < values.Length; i++) {
                dict.TryGetValue(i, out value);
                Type targetType = data.GetType();
                PropertyInfo prop = targetType.GetProperty(value);
                if(values[i]==null)
                {
                    prop.SetValue(data, "NULL",null);
                }
                else
                {
                    prop.SetValue(data, values[i], null);
                }

            }
            dataList.Add(data);
        }

【问题讨论】:

  • 首先,您不能将整个内存用于 c# 进程。我建议您使用 lumenworks csv 解析器。并且不要使用反射。为什么需要反思?它是 csv 文件。你在折磨自己。
  • 查看此内容以获得另一个解释,即您没有自己的所有记忆。 stackoverflow.com/questions/1109558/…
  • 你真的必须将整个解析后的数据保存在内存中吗?也许您应该考虑将它们存储在其他地方(数据库?,带有二进制序列化的文件?...)。您能给我们介绍一下您的 CustomData 类定义吗?
  • 同意。您可以聚合数据列表。
  • 谢谢,但我知道这一点,我只是想知道我是否做错了阅读,似乎很多人在stackoverflow上建议使用streamreader,只是想我可能会以错误的方式使用它。

标签: c#


【解决方案1】:

您在使用流阅读器时似乎没有任何问题,您在内存中读取了一行,然后忘记了。

但是,在 C# 中,字符串在内存中编码为 UTF-16,因此平均一个字符在内存中消耗 2 个字节。

如果您的 CSV 还包含许多要转换为 "NULL" 的空字段,则每个空字段最多添加 7 个字节。

因此,总的来说,由于您基本上将文件中的所有数据都存储在内存中,因此您需要几乎 3 倍于内存中的文件大小也就不足为奇了。

实际的解决方案是通过 N 行来解析数据,处理它们,然后从内存中释放它们。

注意:考虑使用 CSV 解析器,CSV 不仅仅是逗号或分号,如果您的某个字段包含分号、换行符、引号怎么办。 . ?

编辑

实际上每个字符串在内存中最多占用 20+(N/2)*4 个字节,请参阅C# in Depth

【讨论】:

    【解决方案2】:

    好的,这里有几点。

    • 正如 cmets 中指出的那样,x86 下的 .NET 每个进程只能消耗 1.5GBytes,因此请考虑您的 32 位最大内存

    • StreamReader 本身会有开销。我不知道它是否将整个文件缓存在内存中(也许有人可以澄清一下?)。如果是这样,分块读取和处理文件可能是更好的解决方案

    • CustomData 类,它有多少个字段,创建了多少个实例?请注意,x86 中的每个引用都需要 32 位,x64 中的每个引用需要 64 位。因此,如果您有 CustomData 类,它有 10 个 System.Object 类型的字段,则每个 CustomData 类在存储任何数据之前都需要 88 个字节。

    • dataList.Add 在末尾。我假设您要添加到通用列表?如果是这样,请注意 List 使用加倍算法来调整大小。如果列表中有 1GByte 并且它需要多 1 个字节的大小,它将创建一个 2GByte 数组并在调整大小时将 1GByte 复制到 2GByte 数组。所以突然之间 1GByte + 1 字节实际上需要 3GBytes 来操作。另一种选择是使用预先确定大小的数组

    【讨论】:

    • 谢谢老兄!,既然你提到了,我以前就听说过泛型翻倍..
    • 是的,很容易看到 - 下载 ILSpy 并打开 List 的代码并查看。当添加一个新点时,它会检查后备数组的大小,如果它不够大,它会创建一个大小为 2*size 的新数组并执行复制。因此,有时您在内存中有 3*size 个元素只是为了添加一个点!
    猜你喜欢
    • 2018-01-03
    • 2014-01-15
    • 2013-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-19
    • 1970-01-01
    • 2018-12-09
    相关资源
    最近更新 更多