【问题标题】:I have a *.tsv file which contains 32 million records and I need to load them and do search operation我有一个包含 3200 万条记录的 *.tsv 文件,我需要加载它们并进行搜索操作
【发布时间】:2026-01-04 03:30:01
【问题描述】:

当我加载文件时,它会抛出“OutOfMemoryException”。如何有效地加载和搜索?

我正在使用

//to load the file.
var passEngine = new FileHelper<MyClass>.ReadFile().ToList() 
var passList = passEngine.ReadFile("Files/plain_32m.tsv");

或者有其他方法吗?

【问题讨论】:

  • 以流形式打开并在搜索流时搜索。
  • @ForeverZer0 我还需要将它们投射到“MyClass”中。
  • 那很好,只是不要试图一起做。一次读取一个合理的数字,处理,并阅读更多,同时丢弃已经处理的内容。这就是流媒体的完成方式。你几乎永远不会打开一个大文件,即使是这么大的一小部分,也不会全部吃掉。
  • 听起来像是数据库的一个很好的用例......你必须流式传输文件吗?如果您对 Sql Server 进行批量插入,它会很快,然后很容易可索引/可查询(以及具有持久性的好处)
  • 也许您可以为此使用 OLE DB 连接?这将允许您对文件运行查询,就好像它是数据库一样。 *.com/questions/4063685/…

标签: c# .net csv filehelpers


【解决方案1】:

下面的代码将数据添加到数据表中。它还假设第一行包含列的名称

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"Files/plain_32m.tsv";
        static void Main(string[] args)
        {
            int rowCount = 0;
            StreamReader reader = new StreamReader(FILENAME);
            string line = "";
            DataTable dt = new DataTable();
            while ((line = reader.ReadLine()) != null)
            {
                string[] tsv = line.Split(new char[] { '\t' }).ToArray();
                //remove any end spaces from data
                tsv = tsv.Select(x => x.Trim()).ToArray();

                if (++rowCount == 1)
                {
                    foreach (string colName in tsv)
                    {
                        dt.Columns.Add(colName, typeof(string));
                    }
                }
                else
                {
                    dt.Rows.Add(tsv);
                }

            }


        }
    }
}

【讨论】:

    【解决方案2】:

    您可以考虑通过几种方式接近它

    方法一:

    如果它是一次性搜索操作并且只从大文件中挑选一小部分记录,您可以使用流式方法以及 Linq 到对象来做到这一点。有许多开源库可以用来照顾它。

    我将向您展示一个这样的库,Cinchoo ETL

    using (var p = new ChoCSVReader<MyClass>("*** Your CSV File ***")
        .WithFirstLineHeader()
        )
    {
        var subset = p.Where(rec => rec.ID == 100).ToArray(); //You can apply any filter
    }
    

    方法二:

    将文件加载到数据库。如果您的搜索条件很复杂,并且使用索引等改进搜索,这种方法很有用。您可以使用 EF / BulkCopy / ADO.NET 加载文件。 BulkCopy 更适合这种大文件。示例代码展示了如何使用 Bcp 加载文件

    string connectionString = "*** DB Connection String ***";
    using (var p = new ChoCSVReader<MyClass>("*** Your CSV File ***")
        .WithFirstLineHeader()
        )
    {
        using (SqlBulkCopy bcp = new SqlBulkCopy(connectionString))
        {
            bcp.DestinationTableName = "** DB Table Name **";
            bcp.EnableStreaming = true;
            bcp.BatchSize = 10000;
            bcp.BulkCopyTimeout = 0;
            bcp.NotifyAfter = 10;
            bcp.SqlRowsCopied += delegate (object sender, SqlRowsCopiedEventArgs e)
            {
                Console.WriteLine(e.RowsCopied.ToString("#,##0") + " rows copied.");
            };
            bcp.WriteToServer(p.AsDataReader());
        }
    }
    

    将文件加载到数据库后,您可以通过 EF/ADO.NET 等创建索引、查询和过滤数据等操作。

    希望对你有帮助。

    【讨论】:

      【解决方案3】:

      FileHelpers 有一个FileHelpersAsyncEngine,它允许您逐条记录并避免一次读取或写入所有记录。文档是here

      var engine = new FileHelperAsyncEngine<Customer>();
      
      // Read
      using(engine.BeginReadFile("Input.txt"))
      {
          // The engine is IEnumerable
          foreach(Customer cust in engine)
          {
              // your code here
              Console.WriteLine(cust.Name);
          }
      }
      
      
      // Write     
      using(engine.BeginWriteFile("TestOut.txt"))
      {
          var arrayCustomers = GetSomeMoreCustomers(); // a batch at a time
          if (arrayCustomers.Count() > 0)
          { 
              foreach(Customer cust in arrayCustomers)
              {
                  engine.WriteNext(cust);
              }
          }
      }
      

      【讨论】:

        最近更新 更多