【C# WinForms】ファイルの追記された行だけ読みだす(Read only the new lines from a file that is updated regularly)

追加行だけ読み出すクラスの実装

定期的に更新(末尾に追記されていく)ログファイルの追記された行だけ読み出したい

前回読み出した値を始点にして読み出す実装

※改行が\rのファイルには未対応(\r\n,\nはいけるはず)。bom付utfだと最初の1行にbomがついてくる

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace WindowsFormsApp1
{
    class FileReaderOnlyAdd
    {
        private string _filePath;
        private string _fileEncoding;
        private int _nextPos;

        public FileReaderOnlyAdd(string filePath, string fileEncoding )
        {
            this._filePath = filePath;
            this._fileEncoding = fileEncoding;
            this._nextPos = 0;
        }

        public string[] GetAddText()
        {
            string txtAll = null;
            string[] txtLines = null;
            int endlinepos = 0;

            if (File.Exists(this._filePath))
            {
                using (FileStream fs = new FileStream(this._filePath, FileMode.Open, FileAccess.Read))
                {
                    //追加データあり
                    if (fs.Length > this._nextPos)
                    {
                        using (BinaryReader br = new BinaryReader(fs))
                        {
                            //Read済み位置までシーク
                            fs.Seek(this._nextPos, System.IO.SeekOrigin.Begin);

                            //Read済み位置から終点まで取り出す
                            int len = (int)fs.Length - this._nextPos;
                            var bfr = br.ReadBytes(len);
                            //出力済みの行(一番後ろの\n)まで取り出す(末行は出力中の可能性がある)
                            for (int i = bfr.Length - 1; i >= 0; i--)
                            {
                                if (bfr[i] == 0x0a)
                                {
                                    endlinepos = i;
                                    txtAll = System.Text.Encoding.GetEncoding(this._fileEncoding).GetString(bfr, 0, endlinepos + 1);
                                    //末尾の改行を取る(splitすると余計な空要素が出るため)
                                    txtAll = txtAll.TrimEnd('\n');
                                    txtAll = txtAll.TrimEnd('\r');
                                    //改行コードでsplit
                                    txtLines = txtAll.Replace("\r\n", "\n").Split(new[] { '\n', '\r' });
                                    //次の読み出し位置を記憶
                                    this._nextPos += endlinepos + 1;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            return txtLines;
        }
    }
}

実際に使ってみる

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private FileReaderOnlyAdd fr;

        public Form1()
        {
            InitializeComponent();
            fr = new FileReaderOnlyAdd("test.csv", "utf-8");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            timer1.Start();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            string[] txtLines = fr.GetAddText();
            if (txtLines != null)
            {
                foreach (string txt in txtLines)
                {
                    //Console.WriteLine(txt);
                    textBox1.AppendText(txt + "\r\n");
                }
            }
        }
    }
}

ファイル監視間隔が秒オーダーであれば十分使えそう

msオーダーだと間に合わないかも

コメント

タイトルとURLをコピーしました