LINQ:字符串的大列表



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

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

谢谢你的想法!

尝试逐行解析文件,而不是保存到内存中,像这样

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")

和所有行将按需加载,并且在执行期间内存消耗应该相对恒定。

更新:

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

我已经鼓起勇气重构了你的代码。请注意,您仍然需要添加一些检查,这是而不是的最终版本-我只是想展示一般的想法。还要注意,我还没有编译它——因为您可以访问解析器状态,而我对它的类型和值一无所知。代码比你的稍微长一点,但是我从来没有忘记Robert Martin写的《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{}

相关内容

  • 没有找到相关文章