4

How to limit the lines pulled from the document to 15 lines at a time. Right now it displays all the lines at once. thanks.

class Program {
  static void Main(string[] args) {
    string[] lines = System.IO.File.ReadAllLines(
        @"C:\Users\chri749y\Documents\Skrive til fil\Testprogram.txt");

    foreach (string line in lines) {
        Console.WriteLine("{0}", line);
    }

    Console.ReadKey();
  }
}
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • 1
    Are you asking how to read only 15 lines at once from the file (ie to replace your File.ReadAllLines call) OR how to process all the lines in blocks of 15? – tolanj Jun 13 '18 at 11:02
  • @tolanj That's the question. Dmitry interpreted it as the former, and I interpreted it as the latter. I read "at a time" meaning "within the loop" (i.e. "I'm working with a big document and don't want it all in memory at once.") – ProgrammingLlama Jun 13 '18 at 11:11
  • Possible duplicate of [Create batches in linq](https://stackoverflow.com/questions/13731796/create-batches-in-linq) – mjwills Jun 13 '18 at 11:13

2 Answers2

10

If you want top 15 lines only, try Take (Linq) which is specially designed for this:

var lines = System.IO.File
  .ReadLines(@"C:\Users\chri749y\Documents\Skrive til fil\Testprogram.txt")
  .Take(15);

In case you want batch processing i.e. get 0 .. 14 lines then 15 .. 29 lines etc.

// Split input into batches with at most "size" items each
private static IEnumerable<T[]> Batch<T>(IEnumerable<T> lines, int size) {
  List<T> batch = new List<T>(size);

  foreach (var item in lines) {
    if (batch.Count >= size) {
      yield return batch.ToArray();

      batch.Clear();
    }

    batch.Add(item);
  }

  if (batch.Count > 0)   // tail, possibly incomplete batch
    yield return batch.ToArray();
}

Then

var batches = Batch(System.IO.File
  .ReadLines(@"C:\Users\chri749y\Documents\Skrive til fil\Testprogram.txt"),
               15);

foreach (var batch in batches) { // Array with at most 15 items
  foreach (var line in batch) {  
    ... 
  }
}
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • If you call twice in a row `System.IO.File.ReadLines(MyFile).Take(15)` will you get **first** the 15 first line, **then** the lines 16 to 30 ? – Cid Jun 13 '18 at 11:05
  • 3
    @Cid: No; you'll get top `15` lines *twice*; if you want to *skip* 15 lines and only then take 15 lines - `.Skip(15).Take(15)` – Dmitry Bychenko Jun 13 '18 at 11:06
  • 1
    Thanks for your answer, this become easy to implement if you want to get lines block by block, although heavy to process with large files, using `.Skip(BlockNumber++ * BlockSize).Take(BlockSize)` – Cid Jun 13 '18 at 11:15
  • 1
    @Cid: In case of files, `.Skip(n).Take(m)` is *inefficient*: we constantly re-read the file; I've edited the answer: we should read the file just once and form batches while reading. – Dmitry Bychenko Jun 13 '18 at 11:23
2

If I've understood you correctly, you can do this using System.IO.File.ReadLines which lets you stream the file in as an IEnumerable<string>. I've created a custom batching function which will read 15 lines at a time.

static void Main(string[] args)
{
    var lines = System.IO.File.ReadLines(@"C:\Users\chri749y\Documents\Skrive til fil\Testprogram.txt");
    foreach (var batch in Batch(lines, 15))
    {
        foreach (var line in batch)
        {
            Console.WriteLine(line);
        }
        Console.ReadKey();
    }
    Console.ReadKey();
}

This will return a List per batchSize (e.g. 15) lines of the file.

private IEnumerable<List<T>> Batch<T>(IEnumerable<T> source, int batchSize)
{
    if (batchSize < 1)
    {
        throw new ArgumentException("Batch size must be at least 1.", nameof(batchSize));
    }
    var batch = new List<T>(batchSize);
    foreach (var item in source)
    {
        batch.Add(item);
        if (batch.Count == batchSize)
        {
            yield return batch;
            batch = new List<T>(batchSize);
        }
    }
    if (batch.Any())
    {
        yield return batch;
    }
}
ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86