在C#中逐行读取文件

Luc*_*ler 56 c# linq line

我正在尝试阅读一些文本文件,其中每行都需要处理.目前我只使用StreamReader,然后单独读取每一行.

我想知道是否有更有效的方法(在LoC和可读性方面)使用LINQ来做到这一点而不影响运营效率.我看到的例子涉及将整个文件加载到内存中,然后处理它.在这种情况下,我不相信这会非常有效.在第一个示例中,文件可以达到大约50k,而在第二个示例中,不需要读取文件的所有行(大小通常小于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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

第二个例子:

// 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ell 94

您可以使用迭代器块非常轻松地编写基于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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

或让乔恩高兴:

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;
Run Code Online (Sandbox Code Playgroud)

然后你有ReadFrom(...)一个懒惰的评估序列没有缓冲,完美的Where等等.

请注意,如果您使用OrderBy或标准GroupBy,它将必须缓冲内存中的数据; 如果你需要分组和聚合,"PushLINQ"有一些花哨的代码,允许你对数据进行聚合但丢弃它(没有缓冲).乔恩的解释就在这里.

  • Bah,关注点分离 - 将行读取分离为单独的迭代器,并使用正常投影:) (23认同)

Jon*_*eet 23

读取一行并检查它是否为null比检查EndOfStream更简单.

不过,我也有一LineReaderMiscUtil,这使得这一切变得简单许多-基本上它暴露了一个文件(或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
Run Code Online (Sandbox Code Playgroud)

LineReader这个实施的核心是IEnumerable<string>.GetEnumerator:

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

几乎所有其他来源只是提供灵活的设置方式dataSource(这是一个Func<TextReader>).