银河

SKYIV STUDIO

  博客园 :: 首页 ::  ::  :: 订阅 订阅 :: 管理 ::
  105 随笔 :: 2 文章 :: 753 评论 :: 22 引用
     在我写的“推箱子”程序的 DataFile 类中有下面这么一个方法:

    /// <summary>
    
///  删除通关步骤文件
    
/// </summary>
    
/// <param name="level">关数</param>
    private void DeleteStepsFile(int level)
    {
      File.Delete(GetStepsFileName(fileName, level));
    }

    该方法主要用在“编辑”关卡完成后保存数据时删除本关的通关步骤(因为关卡地图都被修改了,原来的通关步骤当然不再适用了)。然而,在一次测试中,发现“编辑”关卡完成后保存数据时居然引发一个“DirectoryNotFoundException”异常。经过查找原因,发现通关步骤文件是保存在“steps”目录下,由于从来没有保存过通关步骤,因此也就没有创建“steps”目录,File.Delete 方法在指定的文件不存在时并不引发异常, 但是如果指定的路径无效,还是会引发 DirectoryNotFoundException 异常。
    后来,将 DataFile.DeleteStepsFile 方法改为下面这个样子就正常了(请参见“使用 C# 开发智手机软件:推箱子(十)”):

    /// <summary>
    
///  删除通关步骤文件
    
/// </summary>
    
/// <param name="level">关数</param>
    private void DeleteStepsFile(int level)
    {
      
// 虽然 File.Delete(): 删除指定的文件。如果指定的文件不存在,则不引发异常。 
      
// 但是: 如果指定的路径无效,还是会引发 DirectoryNotFoundException 异常。
      
// 所以需要先用 File.Exists() 判断一下文件是否存在
      string name = GetStepsFileName(fileName, level);
      
if (File.Exists(name)) File.Delete(name);
    }

     我们来看看 MSDN 上对“File.Delete 方法”的描述:

File.Delete 方法 (System.IO)
删除指定的文件。如果指定的文件不存在,则不引发异常。

命名空间:  System.IO
程序集:  mscorlib(在 mscorlib.dll 中)

语法public static void Delete (
  string path
)

参数path   要删除的文件的名称。

异常
异常类型 条件
ArgumentException path 是一个零长度字符串,仅包含空白或者包含一个或多个由 InvalidPathChars 定义的无效字符。
ArgumentNullException path 为空引用(在 Visual Basic 中为 Nothing)。
DirectoryNotFoundException 指定的路径无效(例如,它位于未映射的驱动器上)。
IOException 指定的文件正在使用中。
NotSupportedException path 的格式无效。
PathTooLongException 指定的路径、文件名或者两者都超出了系统定义的最大长度。例如,在基于 Windows 的平台上,路径必须小于 248 个字符,文件名必须小于 260 个字符。
UnauthorizedAccessException 调用方没有所要求的权限。
- 或 -
path 是一个目录。
- 或 -
path 指定一个只读文件。


备注允许 path 参数指定相对或绝对路径信息。相对路径信息被解释为相对于当前工作目录。若要获取当前工作目录,请参见 GetCurrentDirectory。

有关通用 I/O 任务的列表,请参见 通用 I/O 任务。

Windows NT 4.0 平台说明: Delete 不删除为正常 I/O 打开的文件或已在内存中映射的文件。

    还有“File.Exists 方法”(该方法不会引发异常):

File.Exists 方法 (System.IO)
确定指定的文件是否存在。

命名空间:   System.IO
程序集:   mscorlib(在 mscorlib.dll 中)

语法public static bool Exists (
  string path
)

参数path   要检查的文件。

返回值如果调用方具有要求的权限并且 path 包含现有文件的名称,则为 true;否则为 false。如果 path 为 空引用(在 Visual Basic 中为 Nothing)、无效路径或零长度字符串,则此方法也将返回 false。如果调用方不具有读取指定文件所需的足够权限,则不引发异常并且该方法返回 false,这与 path 是否存在无关。

备注不应使用 Exists 方法来验证路径,此方法仅检查 path 中指定的文件是否存在。将无效路径传递到 Exists 将返回 false。

请注意,在您调用 Exists 方法和对文件执行其他操作(如 Delete)之间,其他进程可能会对文件进行一些处理。建议的编程做法是在 try...catch 块中包装 Exists 方法和对文件采取的操作,如示例中所示。这有助于缩小潜在冲突的范围。Exists 方法只能帮助确保文件是可用的,但无法保证。

允许 path 参数指定相对或绝对路径信息。相对路径信息被解释为相对于当前工作目录。若要获取当前工作目录,请参见 GetCurrentDirectory。

如果 path 描述一个目录,则此方法返回 false。在确定文件是否存在之前,从 path 参数中移除尾随空格。

    现在我们用下面这段程序来测试一下:

using System;
using System.IO;

sealed class Test
{
  
static void Main(string[] args)
  
{
    
try
    
{
      Console.Write(
"请输入要删除的文件名: ");
      
string fileName = Console.ReadLine();
      
if (fileName == "null") fileName = null;
      
if (args.Length < 1 || File.Exists(fileName))
      
{
        File.Delete(fileName);
        Console.WriteLine(
"File.Delete 成功");
      }

    }

    
catch (Exception ex)
    
{
      Console.WriteLine(
"错误: " + ex.ToString());
    }

  }

}


    运行结果如下:

文件名 直接调用 File.Delete 方法
D:\CS\work>test
先调用 File.Exists 方法
D:\CS\work>test with File.Exists
零长度字符串 请输入要删除的文件名:
错误: System.ArgumentException: 路径的形式不合法。
   在 System.IO.Path.NormalizePathFast(String path, Boolean fullCheck)
   在 System.IO.Path.GetFullPathInternal(String path)
   在 System.IO.File.Delete(String path)
   在 Test.Main(String[] args)
请输入要删除的文件名:
非法字符 请输入要删除的文件名: |
错误: System.ArgumentException: 路径中具有非法字符。
   在 System.IO.Path.CheckInvalidPathChars(String path)
   在 System.IO.Path.NormalizePathFast(String path, Boolean fullCheck)
   在 System.IO.Path.GetFullPathInternal(String path)
   在 System.IO.File.Delete(String path)
   在 Test.Main(String[] args)
请输入要删除的文件名: |
空引用 请输入要删除的文件名: null
错误: System.ArgumentNullException: 值不能为空。
参数名: path
   在 System.IO.File.Delete(String path)
   在 Test.Main(String[] args)
请输入要删除的文件名: null
无效的路径 请输入要删除的文件名: none\a.txt
错误: System.IO.DirectoryNotFoundException: 未能找到路径“D:\CS\work\none\a.txt”的一部分。
   在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   在 System.IO.File.Delete(String path)
   在 Test.Main(String[] args)
请输入要删除的文件名: none\a.txt
无效的网络路径 请输入要删除的文件名: \\z\a.txt
错误: System.IO.IOException: 找不到网络路径。

   在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   在 System.IO.File.Delete(String path)
   在 Test.Main(String[] args)
请输入要删除的文件名: \\z\a.txt
格式无效 请输入要删除的文件名: ab:
错误: System.NotSupportedException: 不支持给定路径的格式。
   在 System.Security.Util.StringExpressionSet.CanonicalizePath(String path, Boolean needFullPath)
   在 System.Security.Util.StringExpressionSet.CreateListFromExpressions(String[] str, Boolean needFullPath)
   在 System.Security.Permissions.FileIOPermission.AddPathList(FileIOPermissionAccess access, AccessControlActions control, String[] pathListOrig, Boolean checkForDuplicates, Boolean needFullPath, Boolean copyPathList)
   在 System.Security.Permissions.FileIOPermission..ctor(FileIOPermissionAccess access, String[] pathList, Boolean checkForDuplicates, Boolean needFullPath)
   在 System.IO.File.Delete(String path)
   在 Test.Main(String[] args)
请输入要删除的文件名: ab:
文件名太长 请输入要删除的文件名: -this-string's-length-is-249-
错误: System.IO.PathTooLongException: 指定的路径或文件名太长,或者两者都太长。完全限定文件名必须少于 260 个字符,并且目录名必须少于 248 个字符。
   在 System.IO.Path.NormalizePathFast(String path, Boolean fullCheck)
   在 System.IO.Path.GetFullPathInternal(String path)
   在 System.IO.File.Delete(String path)
   在 Test.Main(String[] args)
请输入要删除的文件名: -this-string's-length-is-249-
正在使用的文件 请输入要删除的文件名: test.exe
错误: System.UnauthorizedAccessException: 对路径“D:\CS\work\test.exe”的访问被拒绝。
   在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   在 System.IO.File.Delete(String path)
   在 Test.Main(String[] args)
请输入要删除的文件名: test.exe
错误: System.UnauthorizedAccessException: 对路径“D:\CS\work\test.exe”的访问被拒绝。
   在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   在 System.IO.File.Delete(String path)
   在 Test.Main(String[] args)
一个目录 请输入要删除的文件名: D:\CS
错误: System.UnauthorizedAccessException: 对路径“D:\CS”的访问被拒绝。
   在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   在 System.IO.File.Delete(String path)
   在 Test.Main(String[] args)
请输入要删除的文件名: D:\CS
只读文件 请输入要删除的文件名: readonly.file
错误: System.UnauthorizedAccessException: 对路径“D:\CS\work\readonly.file”的访问被拒绝。
   在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   在 System.IO.File.Delete(String path)
   在 Test.Main(String[] args)
请输入要删除的文件名: readonly.file
错误: System.UnauthorizedAccessException: 对路径“D:\CS\work\readonly.file”的访问被拒绝。
   在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   在 System.IO.File.Delete(String path)
   在 Test.Main(String[] args)
不存在的文件 请输入要删除的文件名: none.file
File.Delete 成功
请输入要删除的文件名: none.file
正常的文件 请输入要删除的文件名: readwrite1.file
File.Delete 成功
请输入要删除的文件名: readwrite2.file
File.Delete 成功

    可以看出,如果先调用 File.Exists 方法判断一下指定的文件是否存在再决定是否调用 File.Delete 方法,则仅仅在“指定的文件正在使用中”和“指定一个只读文件”这两种情况下会引发异常。而如果直接调用 File.Delete 方法,则在“指定的文件不存在”的情况下不引发异常,但是在“指定的路径无效”的情况下会引发异常。
    实际上,我认为,“指定的路径无效”应该也算“指定的文件不存在”的一种情况。所以,FCL 中的 File.Delete 方法如果按以下原则进行设计则对开发人员更为友好:
    1. File.Delete 方法在“指定的文件不存在”时引发 FileNotFoundException 异常。
    2. File.Delete 方法在“指定的文件不存在”和“指定的路径无效”时不引发异常。
    我更倾向于第二种方案。这样,在大多数情况下,就可以直接调用 File.Delete 方法,而不用先调用 File.Exists 方法。
续篇:Linux 操作系统下 File.Delete 的表现
posted on 2007-09-01 18:18 银河 阅读(3253) 评论(20)  编辑 收藏 网摘 所属分类: .NET Framework

评论

路过,学习学习
  回复  引用    

#2楼  2007-09-01 20:07 lxhvc [未注册用户]
这样封装的话对开发人员来说是用起来更方便了,单是做为一个基本类库,不应当掩盖这类异常。
  回复  引用    

#3楼 [楼主] 2007-09-01 20:23 银河      
@lxhvc
如果说做为一个基本类库, 不应当掩盖这类异常的话, 那么就应该按照正文中所提的第一种方案设计, 在“指定的文件不存在”时引发 FileNotFoundException 异常。
目前 FCL 中的 File.Delete 方法很容易误导开发人员, 因为其在“指定的文件不存在”时不引发异常, 所以开发人员很可能就不先调用 File.Exists 检查一下. 结果正如我在正文中所遇到的一样, 导致应用程序的 BUG.

  回复  引用  查看    

#4楼  2007-09-01 22:46 Cat Chen      
作为基础API,应该向外提供充分的信息表明执行是否成功以及为什么不成功,因此我更倾向于第一种方案。
  回复  引用  查看    

#5楼 [楼主] 2007-09-01 23:45 银河      
@Cat Chen
看来不少朋友还是倾向于第一种方案。:)
不过我认为这两种方案都优于目前 FCL 使用的方案。
  回复  引用  查看    

#6楼  2007-09-02 02:16 deerchao      
第一种方案当然是强一点,但如果是在多线程的情况下即使测试了File.Exists,也难保没有异常扔出来.

其实最想要的还是File.DeleteToTrashBin()..
  回复  引用  查看    

#7楼  2007-09-02 07:40 随风流月      
Try...Catch...Finally...
  回复  引用  查看    

#8楼 [楼主] 2007-09-02 07:41 银河      
@deerchao
深更半夜还在上网?要注意保重身体。 :)

> 但如果是在多线程的情况下即使测试了File.Exists,也难保没有异常扔出来.
这个在 MSDN 文档中 File.Exists 方法的备注里已经说明了。这种情况下引发异常是应该的。
我们讨论的是,如果在我们的应用程序中,明知要去删除一个可能并不存在的文件时,如果因为 MSDN 文档中说 File.Delete 方法在删除指定的文件不存在的情况下不引发异常,而被误导不事先用 File.Exists 方法检测一下文件是否存在,导致应用程序的 BUG。
所以说,正文中提到的两种方案都比目前 FCL 中使用的方案对开发人员更为友好。

> 其实最想要的还是File.DeleteToTrashBin()..
好象在应用程序中删除文件时放入回放站用处并不大吧?


  回复  引用  查看    

#9楼 [楼主] 2007-09-02 07:57 银河      
@deerchao
@随风流月
> 但如果是在多线程的情况下即使测试了File.Exists,也难保没有异常扔出来.
> Try...Catch...Finally...
这些都是正确的观点。
实际上,虽然“推箱子”程序的 DataFile.DeleteStepsFile 方法中没有使用 try 语句块,但是调用它的方法中还是使用了 try 语句块的,使之在意外发生的情况下也能捕获异常,提醒用户使用“菜单 -> 帮助 -> 错误信息”去查看具体发生了什么意外。但是如果明知要去删除的“通关步骤”文件有可能不存在的情况下还引发异常就是应用程序的 BUG 了,因为这无论如何也不能算是意料之外的情况。
  回复  引用  查看    

#10楼 [楼主] 2007-09-02 08:15 银河      
有时候 MSDN 文档也不是非常准确。
例如 MSDN 文档中说 File.Delete 方法在“指定的文件正在使用中”的情况下会引发“IOException”。但正如我在正文中的测试程序所给出的那样,在这种情况下实际上是引发“UnauthorizedAccessException”。(我在 Windows Server 2003 SP2 和 Windows XP Professional SP2 都测试过)
还有,MSDN 文档中说,“在基于 Windows 的平台上,路径必须小于 248 个字符,文件名必须小于 260 个字符。”,否则,File.Delete 方法就会引发“PathTooLongException”。但是我的测试结果是:
Windows Server 2003 SP2: 文件名达到 249 个字符就引发“PathTooLongException”。
Windows XP Professional SP2: 文件名达到 252 个字符就引发“PathTooLongException”。
不知道在朋友们的机器上测试会是什么结果?

  回复  引用  查看    

#11楼 [楼主] 2007-09-02 08:30 银河      
@随风流月
到你的博客去参观了一下,发现你才13周岁,技术水平就这么高了,非常佩服。:)
我的女儿今年也12周岁了,这学期读刚读初一,今天去学校军训了(共三天,今天是最后一天),明天正式开学。但是她还是只知道玩,电脑也只是经常上QQ聊天,玩玩游戏,哦,还会做小报。
看来要抓紧女儿的学习了,以“随风流月”朋友为榜样。

  回复  引用  查看    

如果你喜欢 可以给File.Delete再包装一下啊
  回复  引用    

#13楼 [楼主] 2007-09-02 09:41 银河      
@武眉博<活靶子.NET>
     不错,是可以进行包装一下:
using System.IO;

static class Fcl
{
  
static void DeleteFile(string path)
  
{
    
if (File.Exists(path)) File.Delete(path);
  }

}

    然后就调用 Fcl.DeleteFile 方法就行了。但是这有个严重的缺点,就是原来在很多情况下会引发的异常被掩盖了。还有就是包装一下总比不上 FCL 本身就提供来得方便。
  回复  引用  查看    

#14楼  2007-09-02 10:19 Anders Cui      
@银河
我们所写的方法通常都是基于某些前提(假设和约束)
比如File.Delete如果能够顺利执行
需要 指定的路径要存在,文件不被使用等前提
如果前提不满足
就应当给用户以明确的说明
所以您说的第一种方案更好一些
  回复  引用  查看    

#15楼 [楼主] 2007-09-02 16:41 银河      
写了一个续篇:“Linux 操作系统下 File.Delete 的表现

  回复  引用  查看    

#16楼  2007-09-02 18:20 deerchao      
@银河
如果有真的这个需求的话做起来就比较麻烦了--有些与文件系统有关的软件还是用得到的.
非得PInovke不行.

  回复  引用  查看    

#17楼 [楼主] 2007-09-03 14:55 银河      
@deerchao
是呀,如果要想不掩盖异常而实现方案一或者方案二的话,是有些麻烦。
  回复  引用  查看    

#18楼  2008-04-23 21:32 匿名 [未注册用户]
是否能删除数据库中指定的某行/列数据?
如果能,怎么写路径?
最好写个例子。
谢谢哦
  回复  引用    

#19楼  2008-08-08 16:01 SDF [未注册用户]
--引用--------------------------------------------------
lxhvc: 这样封装的话对开发人员来说是用起来更方便了,单是做为一个基本类库,不应当掩盖这类异常。
--------------------------------------------------------
--引用--------------------------------------------------
lxhvc: 这样封装的话对开发人员来说是用起来更方便了,单是做为一个基本类库,不应当掩盖这类异常。
--------------------------------------------------------
--引用--------------------------------------------------
匿名: 是否能删除数据库中指定的某行/列数据?
<br>如果能,怎么写路径?
<br>最好写个例子。
<br>谢谢哦
--------------------------------------------------------
--引用--------------------------------------------------
匿名: 是否能删除数据库中指定的某行/列数据?
<br>如果能,怎么写路径?
<br>最好写个例子。
<br>谢谢哦
--------------------------------------------------------
--引用--------------------------------------------------
匿名: 是否能删除数据库中指定的某行/列数据?
<br>如果能,怎么写路径?
<br>最好写个例子。
<br>谢谢哦
--------------------------------------------------------
--引用--------------------------------------------------
匿名: 是否能删除数据库中指定的某行/列数据?
<br>如果能,怎么写路径?
<br>最好写个例子。
<br>谢谢哦
--------------------------------------------------------
--引用--------------------------------------------------
匿名: 是否能删除数据库中指定的某行/列数据?
<br>如果能,怎么写路径?
<br>最好写个例子。
<br>谢谢哦
--------------------------------------------------------
--引用--------------------------------------------------
匿名: 是否能删除数据库中指定的某行/列数据?
<br>如果能,怎么写路径?
<br>最好写个例子。
<br>谢谢哦
--------------------------------------------------------
--引用--------------------------------------------------
匿名: 是否能删除数据库中指定的某行/列数据?
<br>如果能,怎么写路径?
<br>最好写个例子。
<br>谢谢哦
--------------------------------------------------------
--引用--------------------------------------------------
匿名: 是否能删除数据库中指定的某行/列数据?
<br>如果能,怎么写路径?
<br>最好写个例子。
<br>谢谢哦
--------------------------------------------------------
--引用--------------------------------------------------
匿名: 是否能删除数据库中指定的某行/列数据?
<br>如果能,怎么写路径?
<br>最好写个例子。
<br>谢谢哦
--------------------------------------------------------
--引用--------------------------------------------------
匿名: 是否能删除数据库中指定的某行/列数据?
<br>如果能,怎么写路径?
<br>最好写个例子。
<br>谢谢哦
--------------------------------------------------------
--引用--------------------------------------------------
匿名: 是否能删除数据库中指定的某行/列数据?
<br>如果能,怎么写路径?
<br>最好写个例子。
<br>谢谢哦
--------------------------------------------------------
--引用--------------------------------------------------
匿名: 是否能删除数据库中指定的某行/列数据?
<br>如果能,怎么写路径?
<br>最好写个例子。
<br>谢谢哦
--------------------------------------------------------
--引用--------------------------------------------------
匿名: 是否能删除数据库中指定的某行/列数据?
<br>如果能,怎么写路径?
<br>最好写个例子。
<br>谢谢哦
--------------------------------------------------------

  回复  引用    


标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-09-02 16:43 编辑过
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接: