Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly handle exceptions when performing file io

Often I find myself interacting with files in some way but after writing the code I'm always uncertain how robust it actually is. The problem is that I'm not entirely sure how file related operations can fail and, therefore, the best way to handle exceptions.

The simple solution would seem to be just to catch any IOExceptions thrown by the code and give the user an "Inaccessible file" error message, but is it possible to get a bit more fine-grained error messages? Is there a way to determine the difference between such errors as a file being locked by another program and the data being unreadable due to a hardware error?

Given the following C# code, how would you handle errors in a user friendly (as informative as possible) way?

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();
   }
}
like image 997
Morten Christiansen Avatar asked Sep 17 '08 19:09

Morten Christiansen


People also ask

How do you handle file exceptions?

To handle exceptions, we can use the Try, Catch, and Throw keywords. These allowed us to perform normal assignments in a Try section and then handle an exception, if any, in a Catch block.

What is a good way to handle exceptions when trying to write a file in Python?

In Python, exceptions can be handled using a try statement. The critical operation which can raise an exception is placed inside the try clause. The code that handles the exceptions is written in the except clause.


4 Answers

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

Yes. Go ahead and catch IOException, and use the Exception.ToString() method to get a relatively relevant error message to display. Note that the exceptions generated by the .NET Framework will supply these useful strings, but if you are going to throw your own exception, you must remember to plug in that string into the Exception's constructor, like:

throw new FileNotFoundException("File not found");

Also, absolutely, as per Scott Dorman, use that using statement. The thing to notice, though, is that the using statement doesn't actually catch anything, which is the way it ought to be. Your test to see if the file exists, for instance, will introduce a race condition that may be rather vexing. It doesn't really do you any good to have it in there. So, now, for the reader we have:

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

In short, for basic file operations:
1. Use using
2, Wrap the using statement or function in a try/catch that catches IOException
3. Use Exception.ToString() in your catch to get a useful error message
4. Don't try to detect exceptional file issues yourself. Let .NET do the throwing for you.

like image 143
Dustman Avatar answered Oct 20 '22 21:10

Dustman


The first thing you should change are your calls to StreamWriter and StreamReader to wrap them in a using statement, like this:

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

This will take care of calling Close and Dispose for you and will actually wrap it in a try/finally block so the actual compiled code looks like this:

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();
}

The benefit here is that you ensure the stream gets closed even if an exception occurs.

As far as any more explicit exception handling, it really depends on what you want to happen. In your example you explicitly test if the file exists and throw a FileNotFoundException which may be enough for your users but it may not.

like image 38
Scott Dorman Avatar answered Oct 20 '22 21:10

Scott Dorman


  • Skip the File.Exists(); either handle it elsewhere or let CreateText()/OpenText() raise it.
  • The end-user usually only cares if it succeeds or not. If it fails, just say so, he don't want details.

I haven't found a built-in way to get details about what and why something failed in .NET, but if you go native with CreateFile you have thousands of error-codes that can tell you what went wrong.

like image 1
Rune Avatar answered Oct 20 '22 21:10

Rune


I don't see the point in checking for existence of a file and throwing a FileNotFoundException with no message. The framework will throw the FileNotFoundException itself, with a message.

Another problem with your example is that you should be using the try/finally pattern or the using statement to ensure your disposable classes are properly disposed even when there is an exception.

I would do this something like the following, catch any exception outside the method, and display the exception's message :

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;
}
like image 1
Joe Avatar answered Oct 20 '22 22:10

Joe