在C#中检查文件的最后修改日期


Answers:


121


3
不幸的事实-GetLastWriteTime无法获取最后的写入时间。见stackoverflow.com/questions/9992223/...
speciesUnknown

如何获得真实的修改时间?我的意思是,即使不进行实际更改即可简单地重新保存,此属性也会更改。有什么方法可以检测到虚假更改?
Mehdi Dehghani

@MehdiDehghani检查文件是否真的被更改的一种好方法是通过检查校验和。校验仅当文件的内容发生变化看改变stackoverflow.com/questions/10520048/...
克里斯蒂安Beekhuizen

63

您只需要File.GetLastWriteTime静态方法。

例:

var lastModified = System.IO.File.GetLastWriteTime("C:\foo.bar");

Console.WriteLine(lastModified.ToString("dd/MM/yy HH:mm:ss"));

但是请注意,在极少数情况下,写入文件时系统不会更新上次修改的时间(这可以作为高频写入的优化(例如日志记录或错误)而有意发生),则此方法将失败,您将需要从系统订阅文件写入通知,并不断进行监听。


1
赞成花时间包括一个代码示例。
Phil Nicholas

遗憾的是,这里有许多答案说GetLastWriteTime。不幸的是,它没有按照它说的去做-它没有得到最后的写入时间。确保唯一的方法是主动观察文件更改。stackoverflow.com/questions/9992223/…–
物种未知

@gburton当然可以!只有在不更新上次修改时间的情况下写入文件的程序(这是非常不标准的)才会导致此方法出现问题。
Noldorin '18 -10-15

@gburton显然。但是您错过了全部要点:仅仅因为它在您的怪异情况下不起作用,并不意味着它在大多数情况下都不起作用。您的例外,而不是常规。它违反惯例。
Noldorin '18 -10-15

1
@Noldorin我的“怪异案例”实际上是一个.net 4.5 Web应用程序,并且据称不能正确更新时间戳的工具(不应被认为是手动操作)是有角度的命令行工具。这是完全正常的情况;这是Microsoft的性能优化,已使GetLastWriteTime无效。检查此SO线程:stackoverflow.com/a/4381256/3931173
未知

20

请注意,函数File.GetLastWriteTime并不总是按预期运行,操作系统有时有时不会立即更新这些值。即使文件已经被修改过,您也可能会获得旧的时间戳记。

行为可能因OS版本而异。例如,此单元测试每次在我的开发人员机器上都能正常运行,但是在我们的构建服务器上始终会失败。

  [TestMethod]
  public void TestLastModifiedTimeStamps()
  {
     var tempFile = Path.GetTempFileName();
     var lastModified = File.GetLastWriteTime(tempFile);
     using (new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.None))
     {

     }
     Assert.AreNotEqual(lastModified, File.GetLastWriteTime(tempFile));
  }

请参阅File.GetLastWriteTime似乎正在返回“过期”值

您的选择:

a)偶尔会有疏忽。

b)建立一个活动组件,以实现观察者模式(例如tcp服务器客户端结构),直接传达更改,而不是写入/读取文件。快速而灵活,但又有依赖性和可能的​​故障点(当然还有一些工作)。

c)通过替换其他进程定期读取的专用信号文件的内容来确保信令过程。它不像轮询过程那样聪明,并且比调用File.GetLastWriteTime要大得多的开销,但是如果不经常从太多地方检查内容,它将可以完成工作。

/// <summary>
/// type to set signals or check for them using a central file 
/// </summary>
public class FileSignal
{
    /// <summary>
    /// path to the central file for signal control
    /// </summary>
    public string FilePath { get; private set; }

    /// <summary>
    /// numbers of retries when not able to retrieve (exclusive) file access
    /// </summary>
    public int MaxCollisions { get; private set; }

    /// <summary>
    /// timespan to wait until next try
    /// </summary>
    public TimeSpan SleepOnCollisionInterval { get; private set; }

    /// <summary>
    /// Timestamp of the last signal
    /// </summary>
    public DateTime LastSignal { get; private set; }

    /// <summary>
    /// constructor
    /// </summary>
    /// <param name="filePath">path to the central file for signal control</param>
    /// <param name="maxCollisions">numbers of retries when not able to retrieve (exclusive) file access</param>
    /// <param name="sleepOnCollisionInterval">timespan to wait until next try </param>
    public FileSignal(string filePath, int maxCollisions, TimeSpan sleepOnCollisionInterval)
    {
        FilePath = filePath;
        MaxCollisions = maxCollisions;
        SleepOnCollisionInterval = sleepOnCollisionInterval;
        LastSignal = GetSignalTimeStamp();
    }

    /// <summary>
    /// constructor using a default value of 50 ms for sleepOnCollisionInterval
    /// </summary>
    /// <param name="filePath">path to the central file for signal control</param>
    /// <param name="maxCollisions">numbers of retries when not able to retrieve (exclusive) file access</param>        
    public FileSignal(string filePath, int maxCollisions): this (filePath, maxCollisions, TimeSpan.FromMilliseconds(50))
    {
    }

    /// <summary>
    /// constructor using a default value of 50 ms for sleepOnCollisionInterval and a default value of 10 for maxCollisions
    /// </summary>
    /// <param name="filePath">path to the central file for signal control</param>        
    public FileSignal(string filePath) : this(filePath, 10)
    {
    }

    private Stream GetFileStream(FileAccess fileAccess)
    {
        var i = 0;
        while (true)
        {
            try
            {
                return new FileStream(FilePath, FileMode.Create, fileAccess, FileShare.None);
            }
            catch (Exception e)
            {
                i++;
                if (i >= MaxCollisions)
                {
                    throw e;
                }
                Thread.Sleep(SleepOnCollisionInterval);
            };
        };
    }

    private DateTime GetSignalTimeStamp()
    {
        if (!File.Exists(FilePath))
        {
            return DateTime.MinValue;
        }
        using (var stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.None))
        {
            if(stream.Length == 0)
            {
                return DateTime.MinValue;
            }
            using (var reader = new BinaryReader(stream))
            {
                return DateTime.FromBinary(reader.ReadInt64());
            };                
        }
    }

    /// <summary>
    /// overwrites the existing central file and writes the current time into it.
    /// </summary>
    public void Signal()
    {
        LastSignal = DateTime.Now;
        using (var stream = new FileStream(FilePath, FileMode.Create, FileAccess.Write, FileShare.None))
        {
            using (var writer = new BinaryWriter(stream))
            {
                writer.Write(LastSignal.ToBinary());
            }
        }
    }

    /// <summary>
    /// returns true if the file signal has changed, otherwise false.
    /// </summary>        
    public bool CheckIfSignalled()
    {
        var signal = GetSignalTimeStamp();
        var signalTimestampChanged = LastSignal != signal;
        LastSignal = signal;
        return signalTimestampChanged;
    }
}

一些测试:

    [TestMethod]
    public void TestSignal()
    {
        var fileSignal = new FileSignal(Path.GetTempFileName());
        var fileSignal2 = new FileSignal(fileSignal.FilePath);
        Assert.IsFalse(fileSignal.CheckIfSignalled());
        Assert.IsFalse(fileSignal2.CheckIfSignalled());
        Assert.AreEqual(fileSignal.LastSignal, fileSignal2.LastSignal);
        fileSignal.Signal();
        Assert.IsFalse(fileSignal.CheckIfSignalled());
        Assert.AreNotEqual(fileSignal.LastSignal, fileSignal2.LastSignal);
        Assert.IsTrue(fileSignal2.CheckIfSignalled());
        Assert.AreEqual(fileSignal.LastSignal, fileSignal2.LastSignal);
        Assert.IsFalse(fileSignal2.CheckIfSignalled());
    }

我的单元测试有完全相同的问题。由于我要测试的代码逻辑并不简单,因此我花了几天的时间才发现上次修改的时间不准确。
gimbup19年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.