0

我是新手,LINQ目前正在使用它来处理 csv 格式的大数据集(半百万条记录)。我正在使用 aStreamReader打开文件并实现IEnumerable<>接口以填充结果。下面你可以看到阅读代码的主要部分:

IEnumerator<Person> IEnumerable<Person>.GetEnumerator()
{
    using (StreamReader streamReader = new StreamReader(filename)){
        streamReader.ReadLine();
        while (!streamReader.EndOfStream){
            string[] values = streamReader.ReadLine().Split(new char[] { ',' });
            Person p = new Person();
            p.Name = values[0];
            p.Age = Convert.ToInt16(values[1]);
            p.Score = Convert.ToDouble(values[2]);
            p.PlotArea = Convert.ToInt16(values[3]);
            p.ForecastConsumption = Convert.ToDouble(values[4]);
            p.Postcode = values[5];
            p.PropertyType = values[6];
            p.Bedrooms = Convert.ToInt16(values[7]);
            p.Occupancy = Convert.ToInt16(values[8]);

            yield return p;
        }
    }
}

这是一个典型的查询:

var query = from person in reader
            where person.Score > 36.55 && person.Bedrooms < 3
            select person;

我的问题是,每次我想运行查询时StreamReader都必须打开文件。有什么方法可以打开文件一次并运行多个查询?

仅供参考,我对 LINQ 印象深刻,运行上面的查询需要 1.2 秒。只是我将为数据集运行很多规则。

4

3 回答 3

2

我的问题是,每次我想运行查询时,StreamReader 都必须打开文件。有什么方法可以打开文件一次并运行多个查询?

那么最简单的方法是将整个文件加载到列表中,例如

var list = reader.ToList();

// Now run multiple queries over list

显然这将占用大量内存,但这将是最简单的方法。如果您想将多个查询连接在一起,您必须准确地确定您想要做什么 - LINQ 中的组合模型主要是将查询操作链接在一起,而不是从同一源构建多个查询。

如果做不到这一点,如果“一次通过多个查询”和“将整个文件加载到内存中”的复杂性都不适合您,那么您可能会被多次加载它所困。

一种可能更节省内存的中间选项是将所有读入内存(因此您只需执行一次磁盘活动),然后多次解析这些行。这在 IO 方面效率更高,但在 CPU 方面更差。

于 2012-05-22T15:50:06.373 回答
1

您的情况将是以下之间的性能权衡:

  1. 将整个文件读入内存,并运行您需要的任何查询
  2. 只需多次迭代文件即可。

如果是后者,请尝试使用File.ReadLineswhich 在文件 IO 上提供一个不错的 IEnumerable 接口:

public Person ReadPerson(string[] personLine)
{
    Person p = new Person();
    p.Name = personLine[0];
    p.Age = Convert.ToInt16(personLine[1]);
    p.Score = Convert.ToDouble(personLine[2]);
    p.PlotArea = Convert.ToInt16(personLine[3]);
    p.ForecastConsumption = Convert.ToDouble(personLine[4]);
    p.Postcode = personLine[5];
    p.PropertyType = personLine[6];
    p.Bedrooms = Convert.ToInt16(personLine[7]);
    p.Occupancy = Convert.ToInt16(personLine[8]);
}

和用法:

var file = File.ReadLines("/filepath/")
    .Select(line => ReadPerson(line.Split(',')));

var query = from person in file
    where person.Score > 36.55 && person.Bedrooms < 3
    select person;
于 2012-05-22T15:51:37.860 回答
1

这应该有效:

  return from line in File.ReadAllLines(filename)
                     let values = line.Split(new char[] { ',' })
                     select new Person{
                Name = values[0];
                Age = Convert.ToInt16(values[1]);
                Score = Convert.ToDouble(values[2]);
                PlotArea = Convert.ToInt16(values[3]);
                ForecastConsumption = Convert.ToDouble(values[4]);
                Postcode = values[5];
                PropertyType = values[6];
                Bedrooms = Convert.ToInt16(values[7]);
                Occupancy = Convert.ToInt16(values[8]);
            };
于 2012-05-22T15:56:02.703 回答