質問

各行を処理する必要があるテキスト ファイルを読み取ろうとしています。現時点では、StreamReader を使用して、各行を個別に読んでいます。

運用効率を損なうことなく、LINQ を使用してこれを行う、(LoC と読みやすさの点で) より効率的な方法があるかどうか疑問に思っています。私が見た例には、ファイル全体をメモリにロードしてから処理することが含まれています。ただし、この場合、それはあまり効率的ではないと思います。最初の例では、ファイルは最大約 50k になる可能性がありますが、2 番目の例では、ファイルのすべての行を読み取る必要はありません (サイズは通常 10k 未満です)。

現在では、このような小さなファイルにはあま​​り問題がないと主張する人もいるかもしれませんが、私はそのようなアプローチは非効率的なコードにつながると考えています。

最初の例:

// Open file
using(var file = System.IO.File.OpenText(_LstFilename))
{
    // Read file
    while (!file.EndOfStream)
    {
        String line = file.ReadLine();

        // Ignore empty lines
        if (line.Length > 0)
        {
            // Create addon
            T addon = new T();
            addon.Load(line, _BaseDir);

            // Add to collection
            collection.Add(addon);
        }
    }
}

2 番目の例:

// Open file
using (var file = System.IO.File.OpenText(datFile))
{
    // Compile regexs
    Regex nameRegex = new Regex("IDENTIFY (.*)");

    while (!file.EndOfStream)
    {
        String line = file.ReadLine();

        // Check name
        Match m = nameRegex.Match(line);
        if (m.Success)
        {
            _Name = m.Groups[1].Value;

            // Remove me when other values are read
            break;
        }
    }
}
役に立ちましたか?

解決

あなたは反復子ブロックを使用してかなり簡単にLINQベースラインリーダーを書くことができます:

static IEnumerable<SomeType> ReadFrom(string file) {
    string line;
    using(var reader = File.OpenText(file)) {
        while((line = reader.ReadLine()) != null) {
            SomeType newRecord = /* parse line */
            yield return newRecord;
        }
    }
}

やジョンを幸せにするためにます:

static IEnumerable<string> ReadFrom(string file) {
    string line;
    using(var reader = File.OpenText(file)) {
        while((line = reader.ReadLine()) != null) {
            yield return line;
        }
    }
}
...
var typedSequence = from line in ReadFrom(path)
                    let record = ParseLine(line)
                    where record.Active // for example
                    select record.Key;

あなたはReadFrom(...)などにぴったりの、バッファリングせずに遅延評価順序としてWhereています。

あなたがOrderByまたは標準GroupByを使用している場合、それはメモリ内のデータをバッファリングしなければならないことに注意してください。必要性は、グループ化と集計ifyou、「PushLINQは、」あなたがデータに集計を行いますが(バッファなし)を破棄することを可能にするためにいくつかの空想のコードを持っています。 Jonの説明ここのです。

他のヒント

これは、ラインを読み、それがEndOfStreamのためのすべての時間を確認することよりも、ヌルだかどうかをチェックするために簡単です。

しかし、私はまた、LineReaderクラスを持っている MiscUtil にこのロットのすべてを作るましたシンプルな - 基本的には、ファイル(またはすることができますFunc<TextReader>などIEnumerable<string>を公開し、あなたはそれの上にLINQのものを行うあなたのようなことを行うことができますので:ます。

var query = from file in Directory.GetFiles("*.log")
            from line in new LineReader(file)
            where line.Length > 0
            select new AddOn(line); // or whatever

LineReaderの心はIEnumerable<string>.GetEnumeratorのこの実装は、次のとおりです。

public IEnumerator<string> GetEnumerator()
{
    using (TextReader reader = dataSource())
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            yield return line;
        }
    }
}

ソースのほとんどすべての残りの部分は、ちょうど(dataSourceある)Func<TextReader>を設定する柔軟な方法を与えている。

:。あなたはそれが処理の継続のために開いているファイルになりますよう、IEnumerable<T>ソリューションに注意する必要があります。

たとえば、マルクGravellの応答でます:

foreach(var record in ReadFrom("myfile.csv")) {
    DoLongProcessOn(record);
}

ファイルは、処理の全体のために開いたままになります。

すべてのあなたの答えをありがとう!私は、ファイルから行を読み取る必要があるだろうと私は主にマルクのいえに焦点を当て、混合物と一緒に行くことにしました。私はあなたが別離はどこにでも必要とされていると主張ことができると思いますが、あわや、人生は短すぎる!

開いているファイルを保つことに関しては、そのコードは、デスクトップアプリケーションの一部であるとして、この場合は問題になるだろうされていません。

最後に、私はあなたのすべて小文字の文字列を使用気づきました。大文字の文字列への参照のみをして、私は資産と非大文字の文字列の間に差があるJavaで知っているが、私はC#の小文字の文字列に思った?

public void Load(AddonCollection<T> collection)
{
    // read from file
    var query =
        from line in LineReader(_LstFilename)
        where line.Length > 0
        select CreateAddon(line);

    // add results to collection
    collection.AddRange(query);
}

protected T CreateAddon(String line)
{
    // create addon
    T addon = new T();
    addon.Load(line, _BaseDir);

    return addon;
}

protected static IEnumerable<String> LineReader(String fileName)
{
    String line;
    using (var file = System.IO.File.OpenText(fileName))
    {
        // read each line, ensuring not null (EOF)
        while ((line = file.ReadLine()) != null)
        {
            // return trimmed line
            yield return line.Trim();
        }
    }
}

.NET 4.0 以降、 File.ReadLines() という方法が利用可能です。

int count = File.ReadLines(filepath).Count(line => line.StartsWith(">"));
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top