hevele
hevele

Reputation: 943

C# Reading Stream

I want to read a .txt file in C# but I will not read all the lines at the same time. For example, consider 500 lines of text file. I want a function to run 25 times and read 20 consecutive lines each time. In the first call of function, lines from 1 to 20 will be read, and second time it is called, 21-40 will be read.

Below simple code does this in c++ but I don't know how to achieve it in C#:

string readLines(ifstream& i)
{
     string totalLine="", line = "";
     for(int i = 0; i < 20; i++){
          getline(i, line);

          totalLine += line;
     }
     return totalLine;
}

int main()
{

     // ...
     ifstream in;
     in.open(filename.c_str());
     while(true){
         string next20 = readLines(in);
         // so something with 20 lines.
     }
     // ...

}

Upvotes: 1

Views: 549

Answers (4)

ClotzA
ClotzA

Reputation: 161

If are trying to call LineRead() the least number times possible and you want minimum memory usage you could first index the lines in your file:

  1. Parse the file one time and Index the position of each line in the FileStream.
  2. Call ReadLine() only at the desired location.

eg:

// Parse the file
var indexes = new List<long>();
using (var fs = File.OpenRead("text.txt"))
{
    indexes.Add(fs.Position);
    int chr;
    while ((chr = fs.ReadByte()) != -1)
    {
        if (chr == '\n')
        {                        
            indexes.Add(fs.Position);
        }
    }
}

int minLine = 21;
int maxLine = 40;

// Read the line
using (var fs = File.OpenRead("text.txt"))
{
    for(int i = minLine ; i <= maxLine ; i++)
    {
        fs.Position = indexes[ i ];
        using (var sr = new StreamReader(fs))
            Console.WriteLine(sr.ReadLine());

}

Cheers !

Upvotes: 2

Matthew Watson
Matthew Watson

Reputation: 109852

You could write a Batch() method like so:

public static IEnumerable<string> Batch(IEnumerable<string> input, int batchSize)
{
    int n = 0;
    var block = new StringBuilder();

    foreach (var line in input)
    {
        block.AppendLine(line);

        if (++n != batchSize)
            continue;

        yield return block.ToString();
        block.Clear();
        n = 0;
    }

    if (n != 0)
        yield return block.ToString();
}

And call it like this:

string filename = "<Your filename goes here>";
var batches = Batch(File.ReadLines(filename), 20);

foreach (var block in batches)
{
    Console.Write(block);
    Console.WriteLine("------------------------");
}

Upvotes: 1

spender
spender

Reputation: 120528

Oops. GroupBy does not evaluate lazily, so this will greedily consume whole file

var twentyLineGroups = 
    File.ReadLines(somePath)
        .Select((line, index) => new {line, index})
        .GroupBy(x => x.index / 20)
        .Select(g => g.Select(x => x.line));

foreach(IEnumerable<string> twentyLineGroup in twentyLineGroups)
{
    foreach(string line in twentyLineGroup)
    {
        //tada!
    }
}

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1503799

There are various options here, but one simple approach would be:

using (var reader = File.OpenText("file.txt"))
{
    for (int i = 0; i < 25; i++)
    {
        HandleLines(reader);
    }
}

...

private void HandleLines(TextReader reader)
{
    for (int i = 0; i < 20; i++)
    {
        string line = reader.ReadLine();
        if (line != null) // Handle the file ending early
        {
            // Process the line
        }
    }
}

Upvotes: 3

Related Questions