文件系统不稳定。这意味着您不能相信一个操作的结果仍然对下一个操作有效,即使它是下一行代码。您不能只说if (some file exists and I have permissions for it) open the file
,也不能说if (some file does not exist) create the file
。您的if
条件的结果总是可能会在代码的两个部分之间更改。操作是截然不同的:不是原子的。
更糟糕的是,问题的本质意味着如果你想要进行这项检查,你可能已经担心或意识到你无法控制的事情可能会发生在文件中。开发环境的本质使得此事件在测试期间不太可能发生,并且很难再现。因此,您不仅会遇到错误,而且在测试时也不会显示错误。
因此,在正常情况下,最好的做法是甚至不尝试检查文件或目录是否存在。相反,将您的开发时间用于处理文件系统中的异常。无论如何,您必须处理这些异常,这样可以更好地利用您的资源。即使异常很慢,检查文件的存在还需要额外的磁盘访问,并且磁盘访问速度很多。在另一个问题中,我甚至对这个效果进行了良好的投票answer。
但我有些疑惑。例如,在.Net中,如果真的总是为真,则.Exists()
方法首先不会出现在API中。还要考虑您期望您的程序需要创建文件的场景。想到的第一个例子是桌面应用程序。此应用程序将默认用户配置文件安装到其主目录,并且每个用户第一次启动应用程序时,它会将此文件复制到该用户的应用程序数据文件夹。它希望该文件在第一次启动时不存在。
那么什么时候可以提前检查文件的存在(或其他属性,如大小和权限)?是否期望在第一次尝试时失败而非成功是一个足够好的经验法则?
答案 0 :(得分:37)
File.Exists方法主要用于在您不打算打开文件时测试文件是否存在。例如,测试是否存在一个锁定文件,它的存在会告诉你一些东西,但其内容并不重要。
如果要打开该文件,则无论先前调用File.Exists的结果如何,都需要处理任何异常。因此,一般来说,在这些情况下调用它没有实际价值。只需在open方法中使用适当的FileMode枚举值并处理任何异常,就像那样简单。
编辑:尽管这是基于.Net API的,但它基于底层系统API。 Windows和Unix都有系统调用(即CreateFile),它们使用等效的FileMode枚举。事实上,在.Net(或Mono)中,FileMode值只是传递给底层系统调用。
答案 1 :(得分:5)
作为一般政策,File.Exists
等方法或WeakReference.Alive
或SomeConcurrentQueue.Count
等属性无法确保“良好”状态存在,但可能有用作为一种确定“坏”状态的手段,而不做任何不必要的(可能适得其反的)工作。在涉及锁(和文件,因为它们通常包括锁)的许多场景中可能出现这种情况。因为所有需要锁定一组资源的例程应该在任何可行的情况下始终以一致的顺序获取对这些资源的锁定,所以可能需要在获取资源之前获取预期存在的一个资源上的锁定。或者可能不存在。在这种情况下,虽然不可能避免可能锁定第一个资源的可能性,但是无法获取第二个资源,然后释放第一个锁而没有对其进行任何有用的工作,在之前检查第二个资源是否存在获得第一个锁定将最大限度地减少不必要的和无用的努力。
答案 2 :(得分:4)
这取决于您的要求,但一种方法是尝试使用某种重试机制获取独占打开文件句柄。一旦掌握了这个句柄,另一个进程就很难(或不可能)删除(或移动)该文件。
我在.NET中使用了类似于以下内容的代码来获取独占文件句柄,我希望其他一些进程可能正在编写文件:
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);
答案 3 :(得分:2)
一个例子:您可以检查是否存在无法打开的文件(例如,由于权限)。
另一个可能更好的例子:您想要检查是否存在Unix设备文件。但绝对不要打开它;打开它有副作用(例如,打开/关闭/dev/st0
将倒带)
答案 4 :(得分:1)
在* nix环境中,检查程序的另一个副本是否已在运行的完善方法是创建锁定文件。因此,检查文件是否存在用于验证这一点。
答案 5 :(得分:1)
我只会检查它是否会丢失(例如应用程序设置),并且只有在我必须阅读文件时才会检查。
如果我必须写入文件,它可能是一个日志文件(所以我可以添加它或创建一个新文件)或者我替换它的内容,所以我不妨重新创建它。
如果我希望该文件存在,那么抛出异常是正确的。然后,异常处理应通知用户或执行恢复。我的意见是,这会产生更清晰的代码。
文件保护(即不覆盖(可能很重要)文件)是不同的,在这种情况下,我总是检查文件是否存在,如果框架不对我这样做(想想SaveFileDialog)
答案 6 :(得分:1)
我认为当您想要确定文件首先存在时,检查是有意义的。正如你所说的设置文件......如果有文件,我会尝试合并现有设置而不是将它们吹走。
其他情况是当用户告诉我对文件执行某些操作时。是的我知道openFileDialog将检查文件是否存在(但这是可选的)。我隐约记得在VB6中,事实并非如此,所以验证文件存在,他们只是告诉我使用是常见的。
我宁愿不按例外编程。
我没有错过这一点。您可能尝试访问该文件,抛出异常,然后当您创建该文件时,该文件已经放在那里。这现在导致您的异常处理代码继续使用fritz。所以我想我们可以在异常处理程序中有一个异常处理程序来捕获文件再次更改...
我宁愿尝试防止异常,也不要用它们来控制逻辑。
此外,另一次检查大小等属性的时间是等待文件操作完成时,是的,你永远不知道但是有一个好的算法,并且根据系统写入文件你可能能够处理一个大量的情况(如果一个系统运行了五年,观察到来自ftp的小文件,并且它使用与文件系统观察者相同的api,然后开始轮询等待文件停止更改,然后再提起事件该文件已准备好被消费。)
答案 7 :(得分:1)
这可能过于简单了,但我认为检查文件是否存在(因此存在.Exists())的主要原因是为了防止意外覆盖现有文件,而不是避免由于尝试访问不存在的或不可访问的文件。
编辑2
事实上,这太简单了,我建议你看看斯蒂芬马丁的回应。
答案 8 :(得分:0)
您可能正在撰写许多可能的应用程序,只需要一个简单的File.Exists即可完成这项工作。如果它是一个只有你的应用程序才会使用的配置文件,那么你就不需要在你的异常处理方面做得太过分了。
虽然您在使用此方法时指出的“缺陷”都是有效的,但并不意味着它们在某些情况下不是可接受的缺陷。
答案 9 :(得分:0)
我认为,只要您知道该文件可能存在或者可能不存在,并且您希望根据文件的存在执行某些备用操作,您应该进行检查,因为在这种情况下,它不是文件的例外条件不存在这不会免除您必须处理异常 - 来自其他人在检查和打开之间删除或创建文件 - 但它使程序的意图清晰,并且不依赖于异常处理来执行流程 - 控制逻辑。
编辑:示例可能是启动时的日志轮换。
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?
}
答案 10 :(得分:0)
虽然这是一篇与语言无关的帖子,但似乎你在谈论.NET。大多数系统(.NET和其他系统)都有更详细的API,以便在打开文件时确定文件是否存在。
您应该做的是调用访问该文件,因为它通常会通过某种错误指示该文件不存在(如果它确实不存在)。在.NET中,您必须通过P / Invoke层并使用CreateFile API函数。如果该函数返回错误ERROR_FILE_NOT_FOUND,那么您知道该文件不存在。如果它成功返回,那么你有一个可以使用的句柄。
这里的要点是它是一个有点原子操作,最终是你正在寻找的。 p>
然后,使用句柄,您可以将其传递给FileStream构造函数并在文件上执行您的工作。
答案 11 :(得分:0)
各种应用程序都包含内置Web服务器。他们在第一次启动时生成自签名SSL证书是很常见的。实现此目的的一种简单方法是检查启动时是否存在证书,如果不存在则创建证书。
理论上,它可以用于检查,而不是以后存在。在这种情况下,当我们尝试收听时,我们会收到错误,但这可以很容易地处理,并不是什么大问题。
它也可能不存在检查,并且以后存在。在这种情况下,它会被新证书覆盖,或者写新证书失败,具体取决于您的策略。第一个是有点烦人,就证书更改引起一些警报而言,但也不是很关键,特别是如果你做了一些记录来指示发生了什么。
而且,在实践中,这两种情况都不太可能出现。
答案 12 :(得分:0)
就像你指出的那样,如果文件丢失,程序应该做什么总是很重要。在我的所有应用程序中,用户始终可以删除配置文件,应用程序将创建一个具有默认值的新文件。没问题。我还发送没有配置文件的应用程序。
但是用户倾向于删除文件,甚至删除不应删除的文件,如序列密钥和模板文件。我总是检查这些文件,因为没有它们,应用程序根本无法运行。我无法从默认情况下创建新的串行密钥。
文件丢失时应该怎么办?您可以执行文件查找或异常处理程序,但真正的问题是:文件丢失时会发生什么?或者文件对应用程序的重要性。在我尝试访问应用程序的任何支持文件之前,我一直在检查。如果文件损坏且无法加载,我会进行错误处理。
答案 13 :(得分:0)
我认为“存在”的原因是确定何时丢失文件而无需创建访问文件或抛出异常所需的所有操作系统内务数据。所以这是一个文件处理优化比什么都重要。
对于单个文件,保存“存在”给出的通常是无关紧要的。如果您检查文件是否存在很多次(例如,搜索#include文件),那么保存可能很重要。
在.Net中,File.Exists的规范没有列出该方法可能抛出的任何异常,不像File.Open列出了9个异常,因此前者的检查肯定会少。
即使“存在”返回true,您仍然需要在打开文件时处理异常,如.Net参考建议的那样。
答案 14 :(得分:0)
我们有一个诊断工具,必须收集一组文件,包括安装程序日志。根据不同的条件,安装程序日志可以位于两个文件夹之一中。更糟糕的是,这两个文件夹中都可能有不同版本的日志。该工具如何找到合适的工具?
如果检查存在,这很简单。如果只有一个,请抓取该文件。如果存在两个,找到哪个具有最新修改时间并获取该文件。这只是正常的做事方式。
答案 15 :(得分:0)
要回答我自己的问题(部分),我想扩展我使用的示例:默认配置文件。
而不是检查它是否在应用启动时存在并尝试在检查失败时复制文件,要做的事情是总是尝试复制文件。您只需以这样的方式执行此操作,即如果文件存在而副本将失败,而不是替换现有文件。这样,您需要做的就是捕获并忽略由于现有文件而导致复制失败时抛出的任何异常。
答案 16 :(得分:0)
使用基本的计算机科学可以轻松解决您的问题......请阅读Semaphores。
(我并不是说听起来像一个混蛋,我只是指着一个简单的答案来解决一个常见的问题。)
答案 17 :(得分:0)
如果你担心其他人删除文件,也许你应该实现某种锁定系统。例如,我曾经处理过C-News的代码,这是一个Usenet新闻服务器。由于它所做的许多事情都可以异步发生,因此它会通过制作临时文件来“锁定”文件或目录,然后将其硬链接到名为“LOCK”的文件。如果链接失败,则意味着该程序的某个其他版本正在写入该目录,否则它就是您的,您可以做自己喜欢的事情。
关于这一点的好处是,大多数程序都是用shell和awk编写的,这是一个非常便携的锁定机制。此外,锁定文件将包含所有者的PID,因此您可以查看现有的锁定文件以查看所有者是否仍在运行。