4

i am new to and i am working on an app that display the time difference from two date on the last two line on a text file.

I want to read the before last line from a file text, i already know how to read the last line but i need to read the before last.

This is my code :

var lastLine = File.ReadAllLines("C:\\test.log").Last();
                richTextBox1.Text = lastLine.ToString();
Reda HellJoka
  • 55
  • 1
  • 9

7 Answers7

5

All the previous answers eagerly load all the file up in memory before returning the requested last lines. This can be an issue if the file is big. Luckily, it is easily avoidable.

public static IEnumerable<string> ReadLastLines(string path, int count)
{
    if (count < 1)
        return Enumerable.Empty<string>();

    var queue = new Queue<string>(count);

    foreach (var line in File.ReadLines(path))
    {
        if (queue.Count == count)
            queue.Dequeue();

         queue.Enqueue(line);
    }

    return queue;
}

This will only keep in memory the last n read lines avoiding memory issues with large files.

InBetween
  • 32,319
  • 3
  • 50
  • 90
  • You still have to iterate over all lines which is inefficient for large files. See the linked questions in my answer. – Staeff Apr 03 '17 at 13:38
  • @Staeff well yes of course, but its orders of magnitude more efficient than actually loading the whole file up in memory which is what the other solutions based on `ReadAllLines` do. – InBetween Apr 03 '17 at 13:39
  • @InBetween But still using `StreamReader` would be more efficient as I stated in my answer. It makes no sense to load whole file when you only need 2 last lines. – mrogal.ski Apr 03 '17 at 13:40
  • @m.rogalski where am I loading up the whole file? `ReadLines` produces an `IEnumerable` that simply spits out the next line in the file as long as there is one. Anyhow, your solution and mine are identical, sorry I didn't read past `StreamReader` because its not necessary, `ReadLines` works just as well. – InBetween Apr 03 '17 at 13:42
  • @InBetween My bad. I though it's `ReadAllLines` like in other answers. – mrogal.ski Apr 03 '17 at 13:44
  • @m.rogalski np. My answer and yours are the same, I'll delete this one because it doesn't bring much more to the table and yours was first (my bad for not reading it completely). Simply edit yours to use `ReadLines`, it reads better and its simpler. – InBetween Apr 03 '17 at 13:45
  • No need to delete any. Both differs in the sense of logical approach. – mrogal.ski Apr 03 '17 at 13:46
4

Since

 File.ReadAllLines("C:\\test.log");

returns an array you can take the last two items of the array:

 var data = File.ReadAllLines("C:\\test.log");

 string last = data[data.Length - 1];
 string lastButOne = data[data.Length - 2];

In general case with long files (and that's why ReadAllLines is a bad choice) you can implement

public static partial class EnumerableExtensions {
  public static IEnumerable<T> Tail<T>(this IEnumerable<T> source, int count) {
    if (null == source)
      throw new ArgumentNullException("source");
    else if (count < 0)
      throw new ArgumentOutOfRangeException("count");
    else if (0 == count)
      yield break;

    Queue<T> queue = new Queue<T>(count + 1);

    foreach (var item in source) {
      queue.Enqueue(item);

      if (queue.Count > count)
        queue.Dequeue();
    }

    foreach (var item in queue)
      yield return item;
  }
}

...

var lastTwolines = File
  .ReadLines("C:\\test.log") // Not all lines
  .Tail(2);
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
2

You can try to do this

var lastLines = File.ReadAllLines("C:\\test.log").Reverse().Take(2).Reverse();

But depending on how large your file is there are probably more efficient methods to process this than reading all lines at once. See Get last 10 lines of very large text file > 10GB and How to read last “n” lines of log file

Community
  • 1
  • 1
Staeff
  • 4,994
  • 6
  • 34
  • 58
  • Reverse the while file, take two elements and reverse it again? Seems odd to me, but should work. – MakePeaceGreatAgain Apr 03 '17 at 13:35
  • The second reverse is only needed if you care about the order of the lines, guessing from her problem description it's probably not necessary but just to give a complete answer I included it. – Staeff Apr 03 '17 at 13:37
2

Simply store the result of ReadAllLines to a variable and than take the two last ones:

var allText = File.ReadAllLines("C:\\test.log");
var lastLines = allText.Skip(allText.Length - 2);
MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
1

You can use Skip() and Take() like

var lastLine = File.ReadAllLines("C:\\test.log");
var data = lastLine.Skip(lastLine.Length - 2);
                richTextBox1.Text = lastLine.ToString();
Rahul
  • 76,197
  • 13
  • 71
  • 125
1

You can use StreamReader in a combination of Queue<string> since you have to read whole file either way.

// if you want to read more lines change this to the ammount of lines you want
const int LINES_KEPT = 2;

Queue<string> meQueue = new Queue<string>();
using ( StreamReader reader = new StreamReader(File.OpenRead("C:\\test.log")) )
{
    string line = string.Empty;
    while ( ( line = reader.ReadLine() ) != null )
    {
        if ( meQueue.Count == LINES_KEPT  )
           meQueue.Dequeue();

        meQueue.Enqueue(line);
    }
}

Now you can just use these 2 lines like such :

string line1 = meQueue.Dequeue(); 
string line2 = meQueue.Dequeue(); // <-- this is the last line.

Or to add this to the RichTextBox :

richTextBox1.Text = string.Empty; // clear the text
while ( meQueue.Count != 0 )
{
    richTextBox1.Text += meQueue.Dequeue(); // add all lines in the same order as they were in file
}

Using File.ReadAllLines will read the whole text and then using Linq will iterate through already red lines. This method does everything in one run.

mrogal.ski
  • 5,828
  • 1
  • 21
  • 30
1
string line;
string[] lines = new string[]{"",""};
int index = 0;
using ( StreamReader reader = new StreamReader(File.OpenRead("C:\\test.log")) )
{
   while ( ( line = reader.ReadLine() ) != null )
   {
       lines[index] = line;
       index = 1-index;
   }
}
// Last Line -1 = lines[index]
// Last line    = lines[1-index]
Gene Stempel
  • 285
  • 1
  • 5