2

If I want to safely try to open a file in D, is the preferred way to either

  1. try to open it, catch exception (and optionally figure out why) if it fails or
  2. check if it exists, is readable and only then open it

I'm guessing the second alternative results in more IO and is more complex right?

tshepang
  • 12,111
  • 21
  • 91
  • 136
Nordlöw
  • 11,838
  • 10
  • 52
  • 99
  • 2
    I don't see any difference between trying to open it and checking if it exists, to me they are the same thing albeit one may throw an exception and the other does not. Whether it is readable or not is going to be difficult and highly variable depending on the file type and the data you want to validate. IMO you would have to do the data validation anyway even if the file existed and is readable. Anyway personally, I always do 1. and this seems to be the most common pattern I have seen to date. – EdChum Sep 19 '13 at 07:45
  • 1
    "and the other does not" - this is not always the case, and relying on such an assumption can lead to [security problems](http://stackoverflow.com/a/85237/21501). – Vladimir Panteleev Sep 19 '13 at 14:58

3 Answers3

6

If the file is expected to be there according to normal program operation and the given user input, then use 1 - just try to open the file and rely on exception handling to handle the exceptional situation that the file is not there.

For example:

/// If the user has a local configuration file in his home directory, open that.
/// Otherwise, open the global configuration file that is a part of the program,
/// and should be installed on all systems where the program is running.
File configFile;
if ("~/.transmogrifier.conf".expandTilde.exists)
    configFile.open("~/.transmogrifier.conf".expandTilde);
else
    configFile.open("/etc/transmogrifier.conf");

Note that using 2 might lead to security issues in your program. For example, if the file is present at the moment when your program checks if the file exists, but is gone when it tries to open it, your program may behave in an unexpected way. If you use 2, make sure that your program still behaves in a desirable way if opening the file fails even though your program just checked that the file exists and is readable.

Vladimir Panteleev
  • 24,651
  • 6
  • 70
  • 114
  • So to sum up, I guess it's all about what's the most probably (expected) outcome of the read: If we expect the file to be there use 1. If we're unsure use 2. – Nordlöw Sep 19 '13 at 11:12
  • 1
    Yes. Just be sure to be ready to handle exceptions anyway when using 2. – Vladimir Panteleev Sep 19 '13 at 14:53
  • 2
    It's never guaranteed that you can open the file even if the check for its existence succeeds, because you may not have permissions to read it, or it may be removed by another process before you open it. So, you always have to be ready for an exception. It's just that checking first avoids an exception being thrown in many cases. – Jonathan M Davis Sep 19 '13 at 18:53
4

Generally, it's better to check whether the file exists first, because it's often very likely that the file doesn't exist, and simply letting it fail when you try and open it is a case of using exceptions for flow control. It's also inefficient in the case where the file doesn't exist, because exceptions are quite expensive in D (though the cost of the I/O may still outweigh the cost of the exception given how expensive I/O is).

It's generally considered bad practice to use exceptions in cases where the exception is likely to be thrown. In those cases, it's far better to return whether the operation succeeded or to check whether the operation is likely to succeed prior to attempting the operation. In the case of opening files, you'd likely do the latter. So, the cleanest way to do what you're trying to do would be to do something like

if(filename.exists)
{
    auto file = File(filename);
    ...
}

or if you want to read the whole file in as a string in one go, you'd do

if(filename.exists)
{
    auto fileContents = readText(filename);
    ...
}

exists and readText are in std.file, and File is in std.stdio.

If you're dealing with a case where it's highly likely that the file will exist and that therefore it's very unlikely that an exception will be thrown, then skipping the check and just trying to open the file is fine. But what you want to avoid is relying on the exception being thrown when it's not unlikely that the operation will fail. You want exceptions to be thrown rarely, so you check that operations will succeed before attempting them if it's likely that they will fail and throw an exception. Otherwise, you end up using exceptions for flow control and harm the efficiency (and maintainability) of your program.

And it's often the case that a file won't be there when you try and open it, so it's usually the case that you should check that a file exists before trying to open it (but it does ultimately depend on your particular use case).

Jonathan M Davis
  • 37,181
  • 17
  • 72
  • 102
4

I'd say you need to be prepared for an exception to be thrown anyway, otherwise you have a race condition (another process may delete the file between the test and the open etc). So it's best just to go ahead and open, then deal with the contingency.

Andrei Alexandrescu
  • 3,214
  • 19
  • 17
  • BTW: Are there any performance hits to enclose a statement in a try if no exceptions are thrown? – Nordlöw Sep 22 '13 at 18:28
  • 1
    It depends on the implementation of exception handling, which in turn depends on the platform and compiler. AFAIK, frame-based approaches (like Windows SEH) offer a non-zero setup time, but faster unwind. Table-based approaches have zero overhead, but longer unwind. Some info [here](http://www.hexblog.com/wp-content/uploads/2012/06/Recon-2012-Skochinsky-Compiler-Internals.pdf). – Vladimir Panteleev Sep 22 '13 at 18:41
  • Which kind does DMD on Linux use? I could always do a benchmark on my own, though. – Nordlöw Sep 22 '13 at 21:49