我有一个应用程序,我正在寻找一个文本文件,如果对文件做了任何更改,我使用OnChanged事件处理程序来处理事件。我正在使用NotifyFilters。LastWriteTime,但是事件仍然被触发两次。这是代码。

public void Initialize()
{
   FileSystemWatcher _fileWatcher = new FileSystemWatcher();
  _fileWatcher.Path = "C:\\Folder";
  _fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
  _fileWatcher.Filter = "Version.txt";
  _fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
  _fileWatcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
   .......
}

在我的情况下,OnChanged被调用两次,当我改变文本文件version.txt并保存它。


当前回答

事件如果没有问,这是一个遗憾,没有现成的解决方案样本f#。 要解决这个问题,这里有我的方法,因为我可以,而且f#是一种很棒的。net语言。

使用FSharp.Control.Reactive包过滤掉重复的事件,它只是响应式扩展的f#包装器。所有这些都可以针对全框架或netstandard2.0:

let createWatcher path filter () =
    new FileSystemWatcher(
        Path = path,
        Filter = filter,
        EnableRaisingEvents = true,
        SynchronizingObject = null // not needed for console applications
    )

let createSources (fsWatcher: FileSystemWatcher) =
    // use here needed events only. 
    // convert `Error` and `Renamed` events to be merded
    [| fsWatcher.Changed :> IObservable<_>
       fsWatcher.Deleted :> IObservable<_>
       fsWatcher.Created :> IObservable<_>
       //fsWatcher.Renamed |> Observable.map renamedToNeeded
       //fsWatcher.Error   |> Observable.map errorToNeeded
    |] |> Observable.mergeArray

let handle (e: FileSystemEventArgs) =
    printfn "handle %A event '%s' '%s' " e.ChangeType e.Name e.FullPath 

let watch path filter throttleTime =
    // disposes watcher if observer subscription is disposed
    Observable.using (createWatcher path filter) createSources
    // filter out multiple equal events
    |> Observable.distinctUntilChanged
    // filter out multiple Changed
    |> Observable.throttle throttleTime
    |> Observable.subscribe handle

[<EntryPoint>]
let main _args =
    let path = @"C:\Temp\WatchDir"
    let filter = "*.zip"
    let throttleTime = TimeSpan.FromSeconds 10.
    use _subscription = watch path filter throttleTime
    System.Console.ReadKey() |> ignore
    0 // return an integer exit code

其他回答

我可以通过添加一个函数来检查缓冲区数组中的重复项来做到这一点。

然后使用计时器在数组X时间内未被修改后执行该操作: -每次有东西写入缓冲区时重置计时器 —对tick执行动作

这还捕获了另一种复制类型。如果修改文件夹内的文件,该文件夹也会引发Change事件。

Function is_duplicate(str1 As String) As Boolean
    If lb_actions_list.Items.Count = 0 Then
        Return False
    Else
        Dim compStr As String = lb_actions_list.Items(lb_actions_list.Items.Count - 1).ToString
        compStr = compStr.Substring(compStr.IndexOf("-") + 1).Trim

        If compStr <> str1 AndAlso compStr.parentDir <> str1 & "\" Then
            Return False
        Else
            Return True
        End If
    End If
End Function

Public Module extentions
<Extension()>
Public Function parentDir(ByVal aString As String) As String
    Return aString.Substring(0, CInt(InStrRev(aString, "\", aString.Length - 1)))
End Function
End Module

一个可能的“黑客”是使用响应式扩展来限制事件,例如:

var watcher = new FileSystemWatcher("./");

Observable.FromEventPattern<FileSystemEventArgs>(watcher, "Changed")
            .Throttle(new TimeSpan(500000))
            .Subscribe(HandleChangeEvent);

watcher.EnableRaisingEvents = true;

在本例中,我将设置为50ms,在我的系统上这就足够了,但更高的值应该更安全。(就像我说的,这仍然是一个“hack”)。

我用了n种更简单的方法。

布尔-如果正在进行某项操作,则为true。当它结束时,false。 在处理之前,将其添加到HashSet。这样我就不会重复元素了。 每隔30分钟,计时器的事件就会运行一次,如果没有作业正在执行,它就会清除列表(just hashset = new hashset)。

我简单地添加了一个dupe检查如下:

 private void OnChanged(object source, FileSystemEventArgs e)
    {
        string sTabName = Path.GetFileNameWithoutExtension(e.Name);
        string sLastLine = ReadLastLine(e.FullPath);
        if(sLastLine != _dupeCheck)
        {
            TabPage tp = tcLogs.TabPages[sTabName];
            TextBox tbLog = (TextBox)tp.Controls[0] as TextBox;

            tbLog.Invoke(new Action(() => tbLog.AppendText(sLastLine + Environment.NewLine)));
            tbLog.Invoke(new Action(() => tbLog.SelectionStart = tbLog.Text.Length));
            tbLog.Invoke(new Action(() => tbLog.ScrollToCaret()));
            _dupeCheck = sLastLine;
        }
    }

    public static String ReadLastLine(string path)
    {
        return ReadLastLine(path, Encoding.Default, "\n");
    }

    public static String ReadLastLine(string path, Encoding encoding, string newline)
    {
        int charsize = encoding.GetByteCount("\n");
        byte[] buffer = encoding.GetBytes(newline);
        using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            long endpos = stream.Length / charsize;
            for (long pos = charsize; pos < endpos; pos += charsize)
            {
                stream.Seek(-pos, SeekOrigin.End);
                stream.Read(buffer, 0, buffer.Length);
                if (encoding.GetString(buffer) == newline)
                {
                    buffer = new byte[stream.Length - stream.Position];
                    stream.Read(buffer, 0, buffer.Length);
                    return encoding.GetString(buffer);
                }
            }
        }
        return null;
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

    private const int WM_VSCROLL = 0x115;
    private const int SB_BOTTOM = 7;

    /// <summary>
    /// Scrolls the vertical scroll bar of a multi-line text box to the bottom.
    /// </summary>
    /// <param name="tb">The text box to scroll</param>
    public static void ScrollToBottom(TextBox tb)
    {
        SendMessage(tb.Handle, WM_VSCROLL, (IntPtr)SB_BOTTOM, IntPtr.Zero);
    }

简单地定义一个全局变量var1 = true。

Private Sub FileWatchman_Changed(ByVal sender As System.Object, ByVal e As System.IO.FileSystemEventArgs) Handles FileWatchman.Changed
   If var1 = true 
       your logic goes here
       var1 = false
   Else
       var1 = true 
   End If
End Sub