3

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

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

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

4

6 回答 6

3

我写了一些非常相似的东西。我使用 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 );
                        }
                    }
                }
            }
        }
于 2008-09-17T19:47:08.037 回答
2

是的,FileSystemWatcher 对您的文件内容一无所知。它会告诉您它是否发生了变化等,但不会告诉您发生了什么变化。

你只是添加到文件中吗?帖子中关于是否添加或也可以删除行有点不清楚。假设它们已附加,则解决方案非常简单,否则您将进行一些比较。

于 2008-09-17T19:23:24.600 回答
2

我认为您应该使用 NTFS Change Journal 或类似的:

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.

You can find a description on TechNet. You will need to use PInvoke in .NET.

于 2008-09-17T20:25:24.513 回答
1

如果当前文本足够小,我会将其保留在内存中,然后使用差异算法检查新文本和以前的文本是否更改。这个库http://www.mathertel.de/Diff/不仅会告诉您发生了一些变化,而且还会告诉您发生了什么变化。因此,您可以将更改的数据插入到数据库中。

于 2008-09-17T19:21:12.997 回答
0

在我的脑海中,你可以存储最后一个已知的文件大小。检查文件大小,当它发生变化时,打开阅读器。

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

于 2008-09-17T19:21:06.383 回答
0

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

您可以控制文件本身吗?您可以稍微更改模型以将文件用作缓冲区。而不是一个文件,有两个。一个是分期,一个是所有处理输出的总和。从“缓冲区”文件中读取所有行,处理它们,然后将它们插入到另一个文件的末尾,该文件是处理的所有行的总和。然后,删除您处理的行。这样,您文件中的所有信息都处于待处理状态。问题是,如果系统不是写(即也删除行),那么它将无法工作。

于 2008-09-17T19:23:37.097 回答