2013-01-17 25 views
3

我正在使用LINQ来解析从csv文件读取的大量字符串列表。 我的代码与100MB文件很好地工作。 但由于堆栈溢出异常无法超越它。 我测试我的代码以500MB的文件,凡在列表中字符串的数量约为400万美元。(以500MB csv文件约400万行)LINQ:大型字符串列表

public List<Metrics> MetricsParser(DateTime StartDate, TimeSpan StartTime, DateTime EndDate, TimeSpan EndTime,int dateIndex,int timeIndex) 
    { 
     DateTime sd = StartDate; 
     DateTime ed = EndDate; 
     TimeSpan st = StartTime; 
     TimeSpan et = EndTime; 
     StreamReader streamReader; 
     List<string> lines = new List<string>(); 


     try 
     { 
      streamReader = new StreamReader("file.csv"); 
      lines.Clear(); 
      while (!streamReader.EndOfStream) 
       lines.Add(streamReader.ReadLine()); 
     } 
     catch (Exception ex) 
     { 
      throw ex; 
     } 
     finally 
     { 
      if (streamReader != null) 
       streamReader.Close(); 
     } 

     IEnumerable<Metrics> parsedFileData = null; 
     parsedFileData = from line in lines 
         let log = line.Split(",") 
         where (!(line.StartsWith("#")) & (line.Length > 0)) 
         let dateVal = _utility.GetDateTime(dateformatType, log[(int)dateIndex], log[(int)timeIndex]) 
         let timeVal = _utility.GetTime(log[(int)timeIndex], timeformatType) 
         where (dateVal >= new DateTime(sd.Year, sd.Month, sd.Day, st.Hours, st.Minutes, st.Seconds) 
           & dateVal <= new DateTime(ed.Year, ed.Month, ed.Day, et.Hours, et.Minutes, et.Seconds)) 
         select new Metrics() 
         { 
          Date = dateVal, 
          Metrics1 = log[(int)Metrics1Index], 
          Metrics2 = (Metrics2Index != null) ? log[(int)Metrics2Index] : "default", 
          Metrics3 = (log[(int)Metrics3Index] == null || log[(int)Metrics3Index] == "") ? "-" : log[(int)Metrics3Index], 
          Metrics4 = (log[(int)Metrics4Index] == null || log[(int)Metrics4Index] == "") ? "-" : log[(int)Metrics4Index], 
          Metrics5 = (log[(int)Metrics5Index] == null || log[(int)Metrics5Index] == "") ? "-" : log[(int)Metrics5Index], 
          Metrics6 = (log[(int)Metrics6Index] == null || log[(int)Metrics6Index] == "") ? "-" : log[(int)Metrics6Index], 
          Metrics7 = (log[(int)Metrics7Index] == null || log[(int)Metrics7Index] == "") ? "-" : log[(int)Metrics7Index], 
          Metrics8 = (log[(int)Metrics8Index] == null || log[(int)Metrics8Index] == "") ? "-" : log[(int)Metrics8Index], 
          Metrics9 = (log[(int)Metrics9Index] == null || log[(int)Metrics9Index] == "") ? "-" : log[(int)Metrics9Index], 
         }; 
     return parsedFileData.ToList(); 
    } 

任何想法如何有更大来实现任务数据。

我按照下面的建议尝试了一些建议,但它也无法克服堆栈溢出异常!

try 
{ 
    streamReader = new StreamReader("file.csv"); 
    while (!streamReader.EndOfStream) 
    { 
     var line = streamReader.ReadLine(); 
     if (!(line.StartsWith("#")) & (line.Length > 0)) 
     { 
      var log = line.Split(","); 
      var dateVal = _utility.GetDateTime(dateformatType, log[(int)dateIndex], log[(int)timeIndex]); 
      parsedData.Add(
         new Metrics() 
         { 
          Date = dateVal, 
          Metrics1 = log[(int)Metrics1Index], 
          Metrics2 = (Metrics2Index != null) ? log[(int)Metrics2Index] : "default", 
          Metrics3 = (log[(int)Metrics3Index] == null || log[(int)Metrics3Index] == "") ? "-" : log[(int)Metrics3Index], 
          Metrics4 = (log[(int)Metrics4Index] == null || log[(int)Metrics4Index] == "") ? "-" : log[(int)Metrics4Index], 
          Metrics5 = (log[(int)Metrics5Index] == null || log[(int)Metrics5Index] == "") ? "-" : log[(int)Metrics5Index], 
          Metrics6 = (log[(int)Metrics6Index] == null || log[(int)Metrics6Index] == "") ? "-" : log[(int)Metrics6Index], 
          Metrics7 = (log[(int)Metrics7Index] == null || log[(int)Metrics7Index] == "") ? "-" : log[(int)Metrics7Index], 
          Metrics8 = (log[(int)Metrics8Index] == null || log[(int)Metrics8Index] == "") ? "-" : log[(int)Metrics8Index], 
          Metrics9 = (log[(int)Metrics9Index] == null || log[(int)Metrics9Index] == "") ? "-" : log[(int)Metrics9Index], 
         } 
         ); 
     } 

    } 
} 

感谢您的想法!

+0

使用泛型'foreach/for'循环来做LINQ,我相信它会更快,更简单,更稳定。同样在这样数量的项目上,你更仔细地去做每一点代码,因为即使是小事情也会大大降低性能,所以仔细考虑铸造和创建一个新对象 – sll

+0

如果你可以单独处理它们,使用相当一些内存,你当然可以仍然去建议 – Vogel612

回答

10

尝试通过行来解析文件中的行,而不是保存到内存中,这样

var parsedFileData = new List<Metrics>(); 

while (!streamReader.EndOfStream) 
{ 
    var line = streamReader.ReadLine(); 

    if(IsLineNeedToBeParsed(line)) 
     parsedFileData.Add(ParseLine(line)); 
} 

哪里ParseLine是方法,其中有你的LINQ查询的内容,但对单行和IsLineNeedToBeParsed是工作你的where条款..正如我注意到的 - 你不做任何连接线。

避免加载整个文件内容,然后执行一些大量的查询,其中包含大量的let子句 - 它将在执行期间占用大量内存。

尝试创建过滤,选择聚合数据然后,如果您仍然不喜欢性能的纯函数,尝试通过添加状态优化查询,消除冗余计算,可能缓存,添加批次等。

一个快速点进行:你应该让文件加载懒,就像这样:

private IEnumerable<string> GetAllLines(string path) 
{ 
    using (StreamReader streamReader = new StreamReader(path)) 
    { 
     while (!streamReader.EndOfStream) 
     { 
      yield return streamReader.ReadLine(); 
     } 
    } 
} 

然后你可以从LINQ查询调用它

from line in GetAllLines("file.csv") 

,所有行会被装上在执行过程中,需求和内存消耗应该相对不变。

UPDATE:

我刚刚发现,File.ReadLines(string path)通过创建ReadLinesIterator内部读取文件懒洋洋地。所以你可以在你的LINQ查询中使用这个调用。

我花了一些勇气重构一下你的代码。请注意,你仍然需要添加一些支票,这是而不是最终版本 - 我只是想表达一般想法。还要注意,我没有编译它 - 因为你有权访问解析器状态,而且我对它的类型和值一无所知。代码比你的代码长一点,但我永远不会忘记罗伯特马丁的Clean Code书,它的重点在于“这不是简短,使代码可读”。如果我在某处错了,请纠正我。

public List<Metrics> MetricsParser(DateTime StartDate, TimeSpan StartTime, DateTime EndDate, TimeSpan EndTime,int dateIndex,int timeIndex) 
{ 
    DateTime sd = StartDate; 
    DateTime ed = EndDate; 
    TimeSpan st = StartTime; 
    TimeSpan et = EndTime; 
    List<Metrics> parsedFileData = new List<Metrics>(); 

    using (StreamReader streamReader = new StreamReader("file.csv")) 
    { 
     while (!streamReader.EndOfStream) 
     { 
      var line = streamReader.ReadLine(); 

      if(IsLineNeedToBeParsed(line)) 
       parsedFileData.Add(ParseLine(line)); 
     } 
    } 

    return parsedFileData; 
} 

private bool IsLineNeedToBeParsed(string line) 
{ 
    return !(line.StartsWith("#")) && (line.Length > 0) && IsInDateRange(line); 
} 

private bool IsInDateRange(string line) 
{ 
    var dateVal = GetDateTime(line); 
    return dateVal >= new DateTime(sd.Year, sd.Month, sd.Day, st.Hours, st.Minutes, st.Seconds) 
     & dateVal <= new DateTime(ed.Year, ed.Month, ed.Day, et.Hours, et.Minutes, et.Seconds); 
} 

private Metrics ParseLine(string line) 
{ 
    var log = line.Split(','); 
    var time = _utility.GetTime(log[(int)timeIndex], timeformatType); 
    var dateVal = GetDateTime(line); 
    return new Metrics{ /* fill values here */ } 
} 

private string[] GetDateTime(string line) 
{ 
    var log = line.Split(','); 
    return _utility.GetDateTime(dateformatType, log[(int)dateIndex], log[(int)timeIndex]); 
} 

public class Metrics{} 
+0

嗨,谢谢你的回应。 – Seenu

+0

在性能方面,我建议使用BufferedStream来封装StreamReader。请参阅http://stackoverflow.com/a/9643111/141172 –

相关问题