使用.NET实时读取文件中的更改

时间:2020-03-05 18:59:43  来源:igfitidea点击:

我有一个.csv文件,该文件经常更新(每分钟约20至30次)。我想将新添加的行写入文件后立即将它们插入数据库。

FileSystemWatcher类侦听文件系统更改通知,并在指定文件发生更改时引发一个事件。问题是FileSystemWatcher无法准确确定添加或者删除了哪些行(据我所知)。

读取这些行的一种方法是保存并比较更改之间的行数,并读取最后一个更改与倒数第二个更改之间的差异。但是,我正在寻找一种更清洁(也许更优雅)的解决方案。

解决方案

回答

我们可以存储最后一个已知的文件大小。检查文件大小,并在文件大小更改时打开阅读器。

然后寻找读者到最后一个文件大小,然后从那里开始阅读。

回答

如果当前文本足够小,我会将其保留在内存中,然后使用diff算法检查新文本和先前文本是否已更改。这个库(http://www.mathertel.de/Diff/)不仅会告诉我们某些更改,而且还更改了。因此,我们可以将更改的数据插入数据库。

回答

是的,FileSystemWatcher对文件的内容一无所知。它会告诉我们是否已更改,等等,但不会更改。

我们只是添加到文件中吗?从帖子中还不清楚是否添加了行或者也可以删除行。假设它们是添加的,则解决方案非常简单,否则我们将进行一些比较。

回答

我们对FileSystemWatcher的看法是正确的。我们可以侦听已创建,已修改,已删除等事件,但不会比引发它们的文件更深入。

我们可以控制文件本身吗?我们可以稍微更改模型以将文件用作缓冲区。而不是一个文件,而要两个。一个是阶段,一个是所有已处理输出的总和。从"缓冲区"文件中读取所有行,进行处理,然后将它们插入另一个文件的末尾,该文件即已处理的所有行的总数。然后,删除我们处理的行。这样,文件中的所有信息都将待处理。要注意的是,如果系统不是写操作(即也删除行),那么它将无法正常工作。

回答

我写的东西很相似。我使用FileSystemWatcher来获取有关更改的通知。然后,我使用FileStream读取数据(在读取新数据之前跟踪文件中我的最后位置,并一直寻找该位置)。然后,将读取的数据添加到缓冲区中,该缓冲区会自动提取完整的行,然后输出到UI。

注意:" this.MoreData(..)是一个事件,该事件的侦听器添加到上述缓冲区中,并处理完整的行提取。

注意:正如已经提到的,只有在修改总是添加到文件的情况下,这才起作用。任何删除都会引起问题。

希望这可以帮助。

public void File_Changed( object source, FileSystemEventArgs e )
    {
        lock ( this )
        {
            if ( !this.bPaused )
            {
                bool bMoreData = false;

                // Read from current seek position to end of file
                byte[] bytesRead = new byte[this.iMaxBytes];
                FileStream fs = new FileStream( this.strFilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite );

                if ( 0 == this.iPreviousSeekPos )
                {
                    if ( this.bReadFromStart )
                    {
                        if ( null != this.BeginReadStart )
                        {
                            this.BeginReadStart( null, null );
                        }
                        this.bReadingFromStart = true;
                    }
                    else
                    {
                        if ( fs.Length > this.iMaxBytes )
                        {
                            this.iPreviousSeekPos = fs.Length - this.iMaxBytes;
                        }
                    }
                }

                this.iPreviousSeekPos = (int)fs.Seek( this.iPreviousSeekPos, SeekOrigin.Begin );
                int iNumBytes = fs.Read( bytesRead, 0, this.iMaxBytes );
                this.iPreviousSeekPos += iNumBytes;

                // If we haven't read all the data, then raise another event
                if ( this.iPreviousSeekPos < fs.Length )
                {
                    bMoreData = true;
                }

                fs.Close();

                string strData = this.encoding.GetString( bytesRead );
                this.MoreData( this, strData );

                if ( bMoreData )
                {
                    File_Changed( null, null );
                }
                else
                {
                    if ( this.bReadingFromStart )
                    {
                        this.bReadingFromStart = false;
                        if ( null != this.EndReadStart )
                        {
                            this.EndReadStart( null, null );
                        }
                    }
                }
            }
        }

回答

我认为我们应该使用NTFS更改日志或者类似的方法:

The change journal is used by NTFS to
  provide a persistent log of all
  changes made to files on the volume.
  For each volume, NTFS uses the change
  journal to track information about
  added, deleted, and modified files.
  The change journal is much more
  efficient than time stamps or file
  notifications for determining changes
  in a given namespace.

我们可以在TechNet上找到说明。我们将需要在.NET中使用PInvoke。