BertAR
BertAR

Reputation: 475

Set position StreamReader doesn't work read append-only text file

I have a append-only log file which is monitored by a FileSystemWatcher. When the file is changed, Read() is called for the LogFile object. The log file should be read line by line.

The goal is to read only changes i.e. lines add to the log file (skip already readed lines). Thus the StreamReader should start to read from the position where it ended on the previous read.

My solution so far doesn't work. When I add

1

2

3

4

line by line in Notepad++ to my textfile & save each time when a line was added, the Debug output is

Initial read
1 //OK
2 //OK
3 //OK
1 //looks like the log file is read again from the beginning
2
3
4

Output should be

Initial read
1
2
3
4

Any ideas to solve this problem?

Console code

public class LogFile
    {
        public List<string> Contents { get; }
        string _fullPath;
        long position;

        public LogFile(string fullPath)
        {
            if (File.Exists(fullPath))
            {
                _fullPath = fullPath;
                Contents = new List<string>();
                Read();
            }
            else
            {
                throw new FileNotFoundException($"{fullPath} not found");
            }
        }


        public void Read(FileSystemWatcher fsw = null)
        {
            if (fsw != null)
                fsw.EnableRaisingEvents = false; //set to false to prevent Changed event be fired twice

            FileStream fs = null;
            StreamReader sr = null;

            try
            {
                fs = new FileStream(_fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                try
                {
                    sr = new StreamReader(fs, Encoding.UTF8);
                    if (Contents.Count == 0)
                    {
                        Debug.WriteLine($"Initial read");
                        AddToContents(_fullPath, sr);
                        position = fs.Length; //store the length of the stream
                    }
                    else
                    {
                        sr.DiscardBufferedData();
                        sr.BaseStream.Seek(position, SeekOrigin.Begin);
                        AddToContents(_fullPath, sr);
                        position = fs.Length; //store the length of the stream
                    }

                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"Error while reading from {_fullPath}");
                    //log exception 
                }
                finally
                {
                    if (sr != null)
                        sr.Close();
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Error while opening {_fullPath}");
                //log exception 
            }
            finally
            {
                if (fs != null)
                    fs.Close();
                if (fsw != null)
                    fsw.EnableRaisingEvents = true; //set raise events for the filesystemwatcher to true 
            }
        }

        private List<string> AddToContents(string fullPath, StreamReader sr)
        {
            List<string> newLines = new List<string>();
            try
            {
                while (!sr.EndOfStream)
                {
                    try
                    {
                        string line = sr.ReadLine();
                        if (line != string.Empty)
                        {
                            Contents.Add(line);
                            newLines.Add(line);
                            Debug.WriteLine(line);
                        }

                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine($"Error processing line in {fullPath}");
                        throw;
                    }
                }
                return newLines;

            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Error while reading from {fullPath}");
                throw;
            }
        }
    }
    class Program
    {
        static LogFile file;
        static FileSystemWatcher fsw;
        static void Main(string[] args)
        {
            string path = @"C:\Temp\test.txt";
            file = new LogFile(path);
            CreateWatcher(path);
            Console.ReadKey();
        }

        private static FileSystemWatcher CreateWatcher(string fileNameFullPath)
        {
            fsw = new FileSystemWatcher(Path.GetDirectoryName(fileNameFullPath)); //constructor only accepts directory path
            fsw.IncludeSubdirectories = false;
            fsw.Filter = Path.GetFileName(fileNameFullPath); //filter for the given file 
            fsw.NotifyFilter = NotifyFilters.LastWrite;
            fsw.EnableRaisingEvents = true;
            fsw.Changed += Fsw_Changed;
            return fsw;
        }

        private static void Fsw_Changed(object sender, FileSystemEventArgs e)
        {
            if (file != null)
                file.Read(fsw);
        }
    }

Upvotes: 1

Views: 299

Answers (1)

Hesam Faridmehr
Hesam Faridmehr

Reputation: 1196

Problem is

 position = fs.Length; //store the length of the stream

You should store current position of the stream into position field not length of stream because sometimes FileStream.Length is zero (I don't know why)

this.position = fs.Position;

and check if FileStream.Length is zero skip that change

 fs = new FileStream(this._fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
        if (fs.Length != 0)
        {
            try
            {
                sr = new StreamReader(fs);
                if (this.Contents.Count == 0)
                {
                   ......

Now it's working

Upvotes: 1

Related Questions