Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# continuously read file

Tags:

c#

file-io

I want to read file continuously like GNU tail with "-f" param. I need it to live-read log file. What is the right way to do it?

like image 295
Dmytro Leonenko Avatar asked Sep 24 '10 21:09

Dmytro Leonenko


2 Answers

You want to open a FileStream in binary mode. Periodically, seek to the end of the file minus 1024 bytes (or whatever), then read to the end and output. That's how tail -f works.

Answers to your questions:

Binary because it's difficult to randomly access the file if you're reading it as text. You have to do the binary-to-text conversion yourself, but it's not difficult. (See below)

1024 bytes because it's a nice convenient number, and should handle 10 or 15 lines of text. Usually.

Here's an example of opening the file, reading the last 1024 bytes, and converting it to text:

static void ReadTail(string filename)
{
    using (FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        // Seek 1024 bytes from the end of the file
        fs.Seek(-1024, SeekOrigin.End);
        // read 1024 bytes
        byte[] bytes = new byte[1024];
        fs.Read(bytes, 0, 1024);
        // Convert bytes to string
        string s = Encoding.Default.GetString(bytes);
        // or string s = Encoding.UTF8.GetString(bytes);
        // and output to console
        Console.WriteLine(s);
    }
}

Note that you must open with FileShare.ReadWrite, since you're trying to read a file that's currently open for writing by another process.

Also note that I used Encoding.Default, which in US/English and for most Western European languages will be an 8-bit character encoding. If the file is written in some other encoding (like UTF-8 or other Unicode encoding), It's possible that the bytes won't convert correctly to characters. You'll have to handle that by determining the encoding if you think this will be a problem. Search Stack overflow for info about determining a file's text encoding.

If you want to do this periodically (every 15 seconds, for example), you can set up a timer that calls the ReadTail method as often as you want. You could optimize things a bit by opening the file only once at the start of the program. That's up to you.

like image 78
Jim Mischel Avatar answered Oct 16 '22 17:10

Jim Mischel


More natural approach of using FileSystemWatcher:

    var wh = new AutoResetEvent(false);
    var fsw = new FileSystemWatcher(".");
    fsw.Filter = "file-to-read";
    fsw.EnableRaisingEvents = true;
    fsw.Changed += (s,e) => wh.Set();

    var fs = new FileStream("file-to-read", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    using (var sr = new StreamReader(fs))
    {
        var s = "";
        while (true)
        {
            s = sr.ReadLine();
            if (s != null)
                Console.WriteLine(s);
            else
                wh.WaitOne(1000);
        }
    }

    wh.Close();

Here the main reading cycle stops to wait for incoming data and FileSystemWatcher is used just to awake the main reading cycle.

like image 40
tsul Avatar answered Oct 16 '22 15:10

tsul