执行文件io时如何正确处理异常

时间:2020-03-05 18:59:47  来源:igfitidea点击:

通常,我发现自己以某种方式与文件进行交互,但是在编写代码之后,我总是不确定它实际上是多么鲁棒。问题是我不确定文件相关操作如何会失败,因此无法确定处理期望的最佳方法。

简单的解决方案似乎只是捕获代码抛出的所有IOException,并为用户提供"无法访问的文件"错误消息,但是有可能获得更多细粒度的错误消息。有没有办法确定文件被另一个程序锁定与由于硬件错误导致数据无法读取等错误之间的区别?

给定以下Ccode,我们将如何以一种用户友好的方式(尽可能提供更多信息)来处理错误?

public class IO
{
   public List<string> ReadFile(string path)
   {
      FileInfo file = new FileInfo(path);

      if (!file.Exists)
      {
         throw new FileNotFoundException();
      }

      StreamReader reader = file.OpenText();
      List<string> text = new List<string>();

      while (!reader.EndOfStream)
      {
         text.Add(reader.ReadLine());
      }

      reader.Close();
      reader.Dispose();
      return text;
   }

   public void WriteFile(List<string> text, string path)
   {
      FileInfo file = new FileInfo(path);

      if (!file.Exists)
      {
         throw new FileNotFoundException();
      }

      StreamWriter writer = file.CreateText();

      foreach(string line in text)
      {
         writer.WriteLine(line);
      }

      writer.Flush();
      writer.Close();
      writer.Dispose();
   }
}

解决方案

回答

我会尝试检查该文件。在调用读/写并在那里响应用户之前存在,而不是因为引发检查变得如此容易而增加了引发错误并稍后捕获错误的开销。我确实知道需要提出错误,但是在这种情况下,简单的检查imho将是更好的解决方案。我的意思是添加另一种方法来检查文件是否存在。

另外,如果我们确实检查了文件是否退出,则知道如果无法写入文件,则其他原因将阻止该文件。我们也可以捕获多个异常,第一个要匹配的异常将被捕获,但是我们可能知道这一点...

回答

我们应该更改的第一件事是对StreamWriter和StreamReader的调用,以将它们包装在using语句中,如下所示:

using (StreamReader reader = file.OpenText())
{
   List<string> text = new List<string>();
   while (!reader.EndOfStream)
   {
      text.Add(reader.ReadLine());
   }
}

这将为我们调用Close和Dispose,并实际上将其包装在try / finally块中,因此实际的已编译代码如下所示:

StreamReader reader = file.OpenText();
try
{
   List<string> text = new List<string>();
   while (!reader.EndOfStream)
   {
      text.Add(reader.ReadLine());
   }
}
finally
{
   if (reader != null)
      ((IDisposable)reader).Dispose();
}

这样做的好处是,即使发生异常,也可以确保流被关闭。

至于任何更明确的异常处理,它实际上取决于我们要发生的事情。在示例中,我们显式测试文件是否存在,并抛出FileNotFoundException,这对于用户可能足够,但可能不足够。

回答

  • 跳过File.Exists();或者在其他地方处理它,或者让CreateText()/ OpenText()提高它。
  • 最终用户通常只关心是否成功。如果失败了,就这么说,他不要细节。

我还没有找到一种内置方法来获取有关.NET失败的原因和原因的详细信息,但是如果使用CreateFile进行本机处理,则会有成千上万个错误代码可以告诉我们出了什么问题。

回答

我将使用using语句简化关闭文件的过程。参见MSDN的Cusing语句

从MSDN:

using (TextWriter w = File.CreateText("log.txt")) {
     w.WriteLine("This is line one");
     w.WriteLine("This is line two");
  }
  using (TextReader r = File.OpenText("log.txt")) {
     string s;
     while ((s = r.ReadLine()) != null) {
        Console.WriteLine(s);
     }
  }

回答

我没有发现检查文件是否存在并抛出FileNotFoundException而没有消息的意义。框架将自身抛出FileNotFoundException,并显示一条消息。

示例的另一个问题是,我们应该使用try / finally模式或者using语句,以确保即使有异常,也可以正确处理可处置类。

我将执行以下操作,捕获方法外部的任何异常,并显示异常的消息:

public IList<string> ReadFile(string path)
{
    List<string> text = new List<string>();
    using(StreamReader reader = new StreamReader(path))
    {
      while (!reader.EndOfStream)      
      {         
         text.Add(reader.ReadLine());      
      }
    }
    return text;
}

回答

也许这不是我们要查找的内容,而是重新考虑使用异常处理的类型。首先,至少在我们将程序员视为用户的情况下,不应将异常处理视为"用户友好"的。

以下文章可能是一个总结:http://goit-postal.blogspot.com/2007/03/brief-introduction-to-exception.html。

回答

...but is it possible to get a bit more fine-grained error messages.

是的。继续并捕获IOException,并使用Exception.ToString()方法获取要显示的相对相关的错误消息。请注意,.NET Framework生成的异常将提供这些有用的字符串,但是如果我们要抛出自己的异常,则必须记住将该字符串插入Exception的构造函数中,例如:

抛出新的FileNotFoundException("找不到文件");`

同样,按照斯科特·多曼(Scott Dorman)的说法,绝对使用该using语句。但是要注意的是," using"语句实际上并没有"捕获"任何东西,这应该是这样。例如,检查文件是否存在的测试将引入竞争状况,这可能会非常麻烦。将其放入其中确实对我们没有任何好处。因此,现在,对于读者来说,我们有:

try {  
    using (StreamReader reader = file.OpenText()) {  
        // Your processing code here  
    }  
} catch (IOException e) {  
    UI.AlertUserSomehow(e.ToString());  
}

简而言之,对于基本文件操作:
1.使用using
2,将using语句或者函数包装在catch / es IOException的try / catch中。
3.在catch中使用Exception.ToString()获得有用的错误消息
4.不要试图自己检测出异常的文件问题。让.NET为我们代劳。