I need to know how to read the last line of a text file. I need to find the line and then process it into a SQL database... I\'ve been reading around and scouring the web
There are two ways: simple and inefficient, or horrendously complicated but efficient. The complicated version assumes a sane encoding.
Unless your file is so big that you really can't afford to read it all, I'd just use:
var lastLine = File.ReadLines("file.txt").Last();
Note that this uses File.ReadLines, not File.ReadAllLines. If you're using .NET 3.5 or earlier you'd need to use File.ReadAllLines
or write your own code - ReadAllLines
will read the whole file into memory in one go, whereas ReadLines
streams it.
Otherwise, the complicated way is to use code similar to this. It tries to read backwards from the end of the file, handling nastiness such as UTF-8 multi-byte characters. It's not pleasant.
I would simply combine File.ReadLines(path) and Enumerable.Last:
String last = File.ReadLines(@"C:\file.txt").Last();
It streams the lines and does not load all into memory as File.ReadAllLines
.
string m = "";
StreamReader r = new StreamReader("file_path");
while (r.EndOfStream == false)
{
m = r.ReadLine();
}
Console.WriteLine("{0}\n", m);
r.Close();
Note: All this code assumes UTF-8. If you need to support a code page that uses double wide chars like Unicode, then you need to add extra checks to the char before and/or after the newline to make sure it is really a newline.
One of the primary use cases of this question is scraping the end of a log file. The other answers unfortunately die a horrible death when log files get into the megabytes. Imagine running every line on every call on a tiny single core VPS... yikes.
The nice thing about UTF-8 is that when you hit a '\n' character, you don't have to worry about any dependent bytes because any byte with the high bit clear in UTF8-8 is simply an ASCII character. Pretty handy!
You could use the solution at 'How to read a text file reversely with iterator in C#', but be aware that the code is fairly complex. If you just need a simple UTF-8 line trailer, this solution will work very well and perform great even on large log files.
If you are monitoring lots of files at once and are using something like a FileSystemWatcher in C#, this performance gain will be very important. I am using very similar code on a cheap single cpu Linux VPS to monitor login failures and put ip addresses in the firewall in my MIT licensed project https://github.com/DigitalRuby/IPBan, using https://github.com/DigitalRuby/IPBan/blob/master/IPBanCore/Core/Utility/LogFileScanner.cs (which handles multiple new lines at once).
You'd be suprised at how large auth.log can get when your SSH port is public facing. If you are reading dozens or even hundreds of files regularly, you'll be really glad you didn't use File.ReadAllLines().Last();
As this is only a page of code, it's a nice balance between simple and very fast.
C# Code ...
/// <summary>
/// Utility class to read last line from a utf-8 text file in a performance sensitive way. The code does not handle a case where more than one line is written at once.
/// </summary>
public static class UTF8FileUtilities
{
/// <summary>
/// Read the last line from the file. This method assumes that each write to the file will be terminated with a new line char ('\n')
/// </summary>
/// <param name="path">Path of the file to read</param>
/// <returns>The last line or null if a line could not be read (empty file or partial line write in progress)</returns>
/// <exception cref="Exception">Opening or reading from file fails</exception>
public static string ReadLastLine(string path)
{
// open read only, we don't want any chance of writing data
using (System.IO.Stream fs = System.IO.File.OpenRead(path))
{
// check for empty file
if (fs.Length == 0)
{
return null;
}
// start at end of file
fs.Position = fs.Length - 1;
// the file must end with a '\n' char, if not a partial line write is in progress
int byteFromFile = fs.ReadByte();
if (byteFromFile != '\n')
{
// partial line write in progress, do not return the line yet
return null;
}
// move back to the new line byte - the loop will decrement position again to get to the byte before it
fs.Position--;
// while we have not yet reached start of file, read bytes backwards until '\n' byte is hit
while (fs.Position > 0)
{
fs.Position--;
byteFromFile = fs.ReadByte();
if (byteFromFile < 0)
{
// the only way this should happen is if someone truncates the file out from underneath us while we are reading backwards
throw new System.IO.IOException("Error reading from file at " + path);
}
else if (byteFromFile == '\n')
{
// we found the new line, break out, fs.Position is one after the '\n' char
break;
}
fs.Position--;
}
// fs.Position will be right after the '\n' char or position 0 if no '\n' char
byte[] bytes = new System.IO.BinaryReader(fs).ReadBytes((int)(fs.Length - fs.Position));
return System.Text.Encoding.UTF8.GetString(bytes);
}
}
}
string last = File.ReadLines(@"C:\file.txt").Last();
string lastsymbol = last[last.Count - 1];
First part:
File.ReadAllLines(@"c:\some\path\file.txt").Last();
or
File.ReadLines(@"c:\some\path\file.txt").Last();
ReadLines is prefered.