代码之家  ›  专栏  ›  技术社区  ›  Joel Coehoorn

什么时候可以检查文件是否存在?

  •  35
  • Joel Coehoorn  · 技术社区  · 17 年前

    文件系统是不稳定的。这意味着您不能相信一个操作的结果对下一个操作仍然有效,即使它是下一行代码。你不能只是说 if (some file exists and I have permissions for it) open the file ,你不能说 if (some file does not exist) create the file . 总有一种可能性,那就是 if 条件将 在代码的两部分之间。操作是不同的:不是原子的。

    更糟糕的是,问题的本质意味着,如果您试图进行此检查,很可能您已经担心或意识到文件可能会发生无法控制的事情。开发环境的性质使得此事件在测试期间不太可能发生,并且很难重现。因此,不仅您有一个bug,而且该bug在测试时不会出现。

    因此,在正常情况下,最好的做法是不检查文件或目录是否存在。相反,将开发时间用于处理文件系统中的异常。无论如何,您都必须处理这些异常,因此这是对资源的更好利用。即使异常速度很慢,检查文件是否存在也需要额外的磁盘访问,而磁盘访问是非常困难的 更慢的。我甚至有一个很好的投票 answer

    但我有些怀疑。例如,在.Net中,如果 总是 没错 .Exists() 预料 您的程序需要创建文件。第一个想到的例子是桌面应用程序。此应用程序将默认用户配置文件安装到其主目录中,每个用户第一次启动应用程序时,会将此文件复制到该用户的应用程序数据文件夹中。它希望该文件在第一次启动时不存在。

    那么,什么时候可以提前检查文件的存在(或其他属性,如大小和权限)呢?期待第一次尝试失败而不是成功是一个足够好的经验法则吗?

    18 回复  |  直到 9 年前
        1
  •  37
  •   Stephen Martin    17 年前

    Exists方法主要用于在不打算打开文件时测试文件是否存在。例如,测试是否存在一个锁定文件,该文件的存在告诉您一些事情,但其内容并不重要。

    编辑:尽管这是用.NETAPI表达的,但它是基于底层系统API的。Windows和Unix都有使用与FileMode枚举等效的系统调用(即CreateFile)。事实上,在.Net(或Mono)中,FileMode值只是传递给底层系统调用。

        2
  •  5
  •   supercat    14 年前

    作为一般政策,方法如下 File.Exists ,或类似 WeakReference.Alive SomeConcurrentQueue.Count 作为确保“好”状态存在的一种手段,它们并不有用,但可以作为确定“坏”状态存在的一种手段,而无需做任何不必要的(可能会适得其反的)工作。在许多涉及锁(和文件,因为它们通常包括锁)的场景中可能会出现这种情况。由于需要锁定一组资源的所有例程都应始终以一致的顺序获取对这些资源的锁定,因此在获取可能存在或可能不存在的资源之前,可能需要获取对预期存在的一个资源的锁定。在这种情况下,虽然无法避免可能会锁定第一个资源,无法获取第二个资源,然后在没有对第一个资源执行任何有用的操作的情况下释放第一个锁,但在获取第一个资源的锁之前检查第二个资源的存在将最大限度地减少不必要和无用的工作。

        3
  •  4
  •   Mitch Wheat    17 年前

    使用某种重试机制打开文件句柄。一旦有了这个句柄,另一个进程将很难(或不可能)删除(或移动)该文件。

    FileInfo fi = new FileInfo(fullFilePath);
    
    int attempts = maxAttempts;
    do
    {
        try
        {
            // Asking to open for reading with exclusive access...
            fs = fi.Open(FileMode.Open, FileAccess.Read, FileShare.None);
        }
        // Ignore any errors... 
        catch {}
    
        if (fs != null)
        {
            break;
        }
        else
        {
            Thread.Sleep(100);
        }
    }
    while (--attempts > 0);
    
        4
  •  2
  •   derobert    17 年前

    例如:您可以检查是否存在无法打开的文件(例如,由于权限)。

    /dev/st0 (将倒带)

        5
  •  1
  •   Sunny Milenov    17 年前

    在*nix环境中,检查程序的另一个副本是否已在运行的一种行之有效的方法是创建一个锁文件。因此,文件存在性检查用于验证这一点。

        6
  •  1
  •   Lennaert    17 年前

    我只会在我希望它丢失(例如,应用程序设置)并且只有在我必须读取文件时才检查它。

    如果我期望该文件存在,则抛出异常是正确的。然后,异常处理应通知用户或执行恢复。我的观点是,这会导致代码更干净。

    文件保护(即不覆盖(可能很重要的)文件)是不同的,在这种情况下,如果框架不为我这样做,我总是检查文件是否存在(想想SaveFileDialog)

        7
  •  1
  •   JoshBerke    17 年前

    我认为当你想确认文件在那里的时候,检查是有意义的。正如你所说的设置文件…如果有一个文件,我会尝试合并现有的设置,而不是吹走它们。

    其他情况是当用户告诉我对文件执行某些操作时。是的,我知道openFileDialog将检查文件是否存在(但这是可选的)。我模模糊糊地记得,在VB6中,情况并非如此,因此验证他们刚刚告诉我要使用的文件是否存在是很常见的。

    编辑

    我宁愿尝试阻止异常,而不是用它们来控制逻辑。

    此外,检查属性(如大小)的另一个时间是在等待文件操作完成时,是的,您永远无法确定,但使用良好的算法,并且根据写入文件的系统,您可能能够处理大量情况(有一个运行了五年的系统,它监视通过ftp发送的小文件,并使用与文件系统监视程序相同的api,然后开始轮询,等待文件停止更改,然后引发文件已准备好使用的事件)。

        8
  •  1
  •   cmsjr    17 年前

    编辑2

    事实上,这太简单了,我建议你看看斯蒂芬·马丁的回答。

        9
  •  0
  •   Paul Tomblin    17 年前

    如果您担心其他人删除该文件,也许您应该实现某种锁定系统。例如,我曾经为一个Usenet新闻服务器C-News编写代码。由于它所做的很多事情都可以异步发生,所以它会通过生成一个临时文件“锁定”一个文件或目录,然后将其硬链接到一个名为“锁定”的文件。如果链接失败,这将意味着程序的其他版本正在写入该目录,否则它是您的,您可以做您喜欢的事情。

    有趣的是,大部分程序都是用shell和awk编写的,这是一种非常便携的锁定机制。此外,锁文件将包含所有者的PID,因此您可以查看现有的锁文件,查看所有者是否仍在运行。

        10
  •  0
  •   sharptooth    17 年前

    我们有一个诊断工具,必须收集一组文件,包括安装程序日志。根据不同的情况,安装程序日志可以位于两个文件夹之一。更糟糕的是,这两个文件夹中可能有不同版本的日志。该工具如何找到合适的工具?

        11
  •  0
  •   casperOne    17 年前

    虽然这是一篇语言不可知的帖子,但你似乎在谈论.NET。大多数系统(.NET和其他系统)都有更详细的api,以便在打开文件时确定文件是否存在。

    有点 原子操作,这最终是你要寻找的。

        12
  •  0
  •   Robin Day    17 年前

    您可能正在编写许多可能的应用程序,一个简单的File.Exists就足以胜任这项工作。如果它是一个只有您的应用程序才会使用的配置文件,那么您就不需要在异常处理中过度使用它。

    虽然您在使用此方法时指出的“缺陷”都是有效的,但这并不意味着它们在某些情况下是不可接受的缺陷。

        13
  •  0
  •   DNS    17 年前

    各种应用程序包括内置的web服务器。他们通常在第一次启动时生成自签名SSL证书。实现这一点的简单方法是在启动时检查证书是否存在,如果不存在,则创建证书。

    从理论上讲,它可能是为了检查而存在的,而不是以后存在的。在这种情况下,当我们尝试倾听时会出现错误,但这很容易处理,也不是什么大问题。

    也有可能它不存在用于检查,而存在于稍后。在这种情况下,它要么被新证书覆盖,要么写入新证书失败,这取决于您的策略。第一个是有点烦人,因为证书更改会引起一些警报,但也不是很关键,特别是如果您做了一些日志记录来指示发生了什么。

    而且,在实践中,这两种情况都极不可能出现。

        14
  •  0
  •   TalkingCode    17 年前

    正如您指出的,如果文件丢失,程序应该做什么总是很重要的。在我的所有应用程序中,用户始终可以删除配置文件,应用程序将使用默认值创建一个新的配置文件。没问题。我还提供了没有配置文件的应用程序。

    但是用户倾向于删除文件,甚至是他们不应该删除的文件,比如序列键和模板文件。我总是检查这些文件,因为没有它们,应用程序根本无法运行。我无法从默认设置创建新的串行密钥。

        15
  •  0
  •   tvanfosson    17 年前

    编辑

      try
      {
           if (File.Exists("app.log"))
           {
               RotateLogs();
           }
    
           log = File.Open("app.log", FileMode.CreateNew );
      }
      catch (IOException)
      {
         ...another writer, perhaps?
      }
      catch (UnauthorizedAccessException)
      {
         ...maybe I should have used runas?
      }
    
        16
  •  0
  •   Joel Coehoorn    17 年前

    为了回答我自己的问题(部分),我想扩展一下我使用的示例:默认配置文件。

    总是 尝试复制该文件。如果文件存在,而不是替换现有文件,则复制将失败。这样,您只需捕获并忽略由于现有文件而导致复制失败时引发的任何异常。

        17
  •  0
  •   Kladskull    17 年前

    Semaphores .

    (我并不是想让你听起来像个混蛋,我只是给你指出了一个常见问题的简单答案)。

        18
  •  0
  •   Tim Cooper    13 年前

    我认为“存在”的原因是确定文件何时丢失,而不需要创建访问文件所需的所有操作系统内部管理数据或抛出异常。因此,这是一个文件处理优化比任何其他。

    对于单个文件,“Exists”给出的保存通常是无关紧要的。如果您多次检查某个文件是否存在(例如,搜索#include files),则保存可能非常重要。

    在.Net中,File.Exists的规范没有列出该方法可能引发的任何异常,这与例如File.Open列出九个异常不同,因此在前者中进行的检查肯定较少。