0

私は初めてでLINQ、現在、csv形式(50万レコード)の大きなデータセットを処理するためにそれを使用しています。結果を入力するために、を使用しStreamReaderてファイルを開き、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の構成モデルは、同じソースから複数のクエリを作成するのではなく、クエリ操作をチェーン化することを目的としています。

それができない場合、「1回のパスで複数のクエリを実行する」または「ファイル全体をメモリにロードする」の複雑さがうまくいかない場合は、複数回のロードでスタックする可能性があります。

メモリ効率が高い可能性のある中間オプションの1つは、すべてのをメモリに読み込む(つまり、ディスクアクティビティを1回だけ実行する)が、それらの行を複数回解析することです。これは、IOの観点からははるかに効率的ですが、CPUの観点からはさらに悪くなります。

于 2012-05-22T15:50:06.373 に答える
1

あなたの状況は、次の間のパフォーマンスのトレードオフになります。

  1. ファイル全体をメモリに読み込み、必要なクエリを実行する
  2. ファイルを複数回繰り返すだけです。

後者の場合は、File.ReadLinesファイル 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

This should work:

  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 に答える