IT story

C #에서 한 줄씩 파일 읽기

hot-time 2020. 12. 29. 07:56
반응형

C #에서 한 줄씩 파일 읽기


각 줄을 처리해야하는 일부 텍스트 파일을 읽으려고합니다. 현재 저는 StreamReader를 사용하고 있으며 각 줄을 개별적으로 읽습니다.

운영 효율성을 손상시키지 않고 LINQ를 사용하여이를 수행하는 더 효율적인 방법 (LoC 및 가독성 측면에서)이 있는지 궁금합니다. 내가 본 예제는 전체 파일을 메모리에로드 한 다음 처리하는 것과 관련이 있습니다. 그러나이 경우에는 이것이 매우 효율적이라고 생각하지 않습니다. 첫 번째 예에서 파일은 최대 약 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);
        }
    }
}

두 번째 예 :

// 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;
        }
    }
}

또는 Jon을 행복하게 만들기 위해 :

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데이터를 메모리에 버퍼링해야합니다. 그룹화 및 집계가 필요한 경우 "PushLINQ"에는 데이터에 대한 집계를 수행 할 수 있지만 버퍼링이없는 멋진 코드가 있습니다. Jon의 설명 은 여기에 있습니다 .


줄을 읽고 항상 EndOfStream을 확인하는 것보다 null인지 여부를 확인하는 것이 더 간단합니다.

However, I also have a LineReader class in MiscUtil which makes all of this a lot simpler - basically it exposes a file (or a Func<TextReader> as an IEnumerable<string> which lets you do LINQ stuff over it. So you can do things like:

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

The heart of LineReader is this implementation of IEnumerable<string>.GetEnumerator:

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

Almost all the rest of the source is just giving flexible ways of setting up dataSource (which is a Func<TextReader>).


NOTE: You need to watch out for the IEnumerable<T> solution, as it will result in the file being open for the duration of processing.

For example, with Marc Gravell's response:

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

the file will remain open for the whole of the processing.


Thanks all for your answers! I decided to go with a mixture, mainly focusing on Marc's though as I will only need to read lines from a file. I guess you could argue seperation is needed everywhere, but heh, life is too short!

Regarding the keeping the file open, that isn't going to be an issue in this case, as the code is part of a desktop application.

Lastly I noticed you all used lowercase string. I know in Java there is a difference between capitalised and non capitalised string, but I thought in C# lowercase string was just a reference to capitalised String?

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();
        }
    }
}

Since .NET 4.0, the File.ReadLines() method is available.

int count = File.ReadLines(filepath).Count(line => line.StartsWith(">"));

ReferenceURL : https://stackoverflow.com/questions/1271225/reading-a-file-line-by-line-in-c-sharp

반응형