使用C#读取大型文本文件时出现System.OutofMemoryException

时间:2016-04-06 19:29:47

标签: c# datatable streamreader

我有代码读取文本文件并填充.Net数据表。当代码读取具有100,000行数据的较小文本文件时,代码工作正常。 (请参阅下面的代码段)当我尝试读取更大的文本文件大小(如200MB)并拥有360万行数据时,会抛出System.OutofMemoryException异常。想要一种有效的方法将大数据读入某个块。

        using (var stream = File.Open(filePath, FileMode.Open))
        {
            var content = new StreamContent(stream);
            var fileStream = content.ReadAsStreamAsync().Result;

            if (fileStream == null) throw new ArgumentException(Constants.FileEmptyErrorMessage);

            using (var bs = new BufferedStream(fileStream))
            {
                using (var reader = new StreamReader(bs, Encoding.GetEncoding(Constants.IsoEncoding)))
                {


                    while (!reader.EndOfStream)
                    {
                        var line = reader.ReadLine();
                        if (!String.IsNullOrEmpty(line))
                        {
                            string[] rows = line.Trim().Split(new char[] { ';' }, StringSplitOptions.None);

                            DataRow dr = Table.NewRow();
                            dr[Constants.Percepcion] = rows[0];
                            dr[Constants.StartDate] = DateTime.ParseExact(rows[2].ToString(), "ddMMyyyy",
                                CultureInfo.InvariantCulture);
                            dr[Constants.EndDate] = DateTime.ParseExact(rows[3].ToString(), "ddMMyyyy",
                                CultureInfo.InvariantCulture);
                            dr[Constants.CID] = rows[4];
                            dr[Constants.Rate] = rows[8];

                            Table.Rows.Add(dr);
                        }
                    }
                }
            }
        }

3 个答案:

答案 0 :(得分:0)

我可以看到内存泄漏不是因为你已经逐行读取整个文件var line = reader.ReadLine();。我认为泄漏是因为数据表Table的大小,因为它包含整个文件的所有数据 我建议其中一个选择:
1.如果要对数据表的行执行聚合函数,只需执行它们(如设置整数计数器或双倍max_columnX)而不保留整行。
2.如果你真的需要保留所有行。创建一个数据库(MSSQL / MYSQL或任何)并逐行读取文件 - 就像这样 - 并将这些数据插入数据库。然后用您的标准查询数据库 3.您可以将整个文件批量插入数据库,而无需通过C#应用程序进行处理。这是一个SQL SERVER example

BULK INSERT AdventureWorks2012.Sales.SalesOrderDetail
   FROM 'f:\orders\lineitem.tbl'
   WITH
     (
        FIELDTERMINATOR =';',
        ROWTERMINATOR = '\n',
        FIRE_TRIGGERS
      );

编辑: 您可以附加内存分析器以查找确切占用大内存的内容并将其添加到问题中。这将有助于获得更好的答案。

答案 1 :(得分:0)

如果改变BufferedStream的默认缓冲区大小,那么它应该为您加载更大的文件,效率更高。 E.g。

using (var bs = new BufferedStream(fileStream, 1024))
{
    // Code here.
}

您可以通过简单地使用FileStream来指定缓冲区大小,而不是BufferedStream。有关详细信息,请参阅this MSDN blog regarding it

答案 2 :(得分:0)

这是我为阅读大文本文件所做的工作。无需使用缓冲蒸汽。

var filteredTextFileData = (from textFileData in File.ReadAllLines(_filePathList[0]).Skip(1).Where(line => !string.IsNullOrEmpty(line))
                    let textline = textFileData.Split(';')
                    let startDate = DateTime.ParseExact(textline[2].ToString(), Constants.DayMonthYearFormat, CultureInfo.InvariantCulture)
                    let endDate = !string.IsNullOrEmpty(textline[3]) ? DateTime.ParseExact(textline[3], Constants.DayMonthYearFormat, CultureInfo.InvariantCulture) : (DateTime?)null
                    let taxId = textline[0]
                    join accountList in _accounts.AsEnumerable()
                    on taxId equals accountList.Field<string>(Constants.Comments)
                    where endDate == null || endDate.Value.Year > DateTime.Now.Year || (endDate.Value.Year == DateTime.Now.Year && endDate.Value.Month >= DateTime.Now.Month)
                    select new RecordItem()
                    {
                        Type = Constants.Regular,
                        CustomerTaxId = taxId,
                        BillingAccountNumber = accountList.Field<Int64>(Constants.AccountNo).ToString(),
                        BillingAccountName = accountList.Field<string>(Constants.BillCompany),
                        StartDate = DateTime.Compare(startDate, accountList.Field<DateTime>(Constants.DateActive)) < 1 ? accountList.Field<DateTime>(Constants.DateActive) : startDate,
                        EndDate = endDate,
                        OverrideRate = 0,
                        Result = Constants.NotStarted,
                        TaxCode = _taxCode,
                        ImpliedDecimal = 4
                    }).ToList();