Open a file, or do something else if the file does not exist

  • A+
Category:Languages

I want to open a file and read the contents, or do something else if the file does not exist.

The former can be accomplished easily enough: with-open-file.

For the latter, my first thought was to use handler-case to catch the error, but SBCL says it's SB-INT:SIMPLE-FILE-ERROR, which sounds like a compiler internal symbol, therefore presumably nonportable.

What's the portable way to do this?

 


Use :if-does-not-exist nil (see OPEN):

(defun test (path)   (with-open-file (stream path                           :if-does-not-exist nil                           :element-type '(unsigned-byte 8))     (if stream         (read-byte stream)         :something-else))) 

With a non-existent pathname:

(test #P"/hopefully/path/does/not/exist") => :SOMETHING-ELSE 

With an existing pathname:

(test #P"/dev/urandom") => 123 

You can also use PROBE-FILE to check whether a file exists, as Rainer explained, but then you risk having the file being deleted by another process, after probe-file returns successfully but before actually opening it.

For the latter, my first thought was to use handler-case to catch the error, but SBCL says it's SB-INT:SIMPLE-FILE-ERROR, which sounds like a compiler internal symbol, therefore presumably nonportable.

When you catch an implementation-specific error, you can try to inspect its class hierarchy to find the closest superclass whose name belongs to the Common-Lisp package:

CL-USER> (inspect (find-class 'SB-INT:SIMPLE-FILE-ERROR))   The object is a STANDARD-OBJECT of type SB-PCL::CONDITION-CLASS. 0. %TYPE: (CLASS #<SB-PCL::CONDITION-CLASS SB-INT:SIMPLE-FILE-ERROR>) ... 5. DIRECT-SUPERCLASSES: (#<SB-PCL::CONDITION-CLASS COMMON-LISP:SIMPLE-CONDITION>                          #<SB-PCL::CONDITION-CLASS COMMON-LISP:FILE-ERROR>) 6. DIRECT-SUBCLASSES: NIL ... 11. %CLASS-PRECEDENCE-LIST: (#<SB-PCL::CONDITION-CLASS SB-INT:SIMPLE-FILE-ERROR>                              #<SB-PCL::CONDITION-CLASS COMMON-LISP:SIMPLE-CONDITION>                              #<SB-PCL::CONDITION-CLASS COMMON-LISP:FILE-ERROR>                              #<SB-PCL::CONDITION-CLASS COMMON-LISP:ERROR>                              #<SB-PCL::CONDITION-CLASS COMMON-LISP:SERIOUS-CONDITION>                              #<SB-PCL::CONDITION-CLASS COMMON-LISP:CONDITION>                              #<SB-PCL::SLOT-CLASS SB-PCL::SLOT-OBJECT>                              #<SB-PCL:SYSTEM-CLASS COMMON-LISP:T>) ... 

What the above says is that in SBCL, SB-INT:SIMPLE-FILE-ERROR has two direct superclasses, one of them being COMMON-LISP:FILE-ERROR.

Here is another example, using CCL:

? (handler-case (open #P"/tmp/foo/bar/baz/foo") (error (e) (inspect e)))  [0]     #<CCL::SIMPLE-FILE-ERROR #x3020004DE7ED> [1]     Class: #<STANDARD-CLASS CCL::SIMPLE-FILE-ERROR> [2]     Wrapper: #<CCL::CLASS-WRAPPER CCL::SIMPLE-FILE-ERROR #x3020000F62DD>         Instance slots [3]     PATHNAME: #P"/tmp/foo/bar/baz/foo" [4]     CCL::ERROR-TYPE: "No such file or directory : ~s" [5]     CCL::FORMAT-CONTROL: #<Unbound> [6]     CCL::FORMAT-ARGUMENTS: (NIL) Inspect> 1 [0]     #<STANDARD-CLASS CCL::SIMPLE-FILE-ERROR> [1]     Class: #<STANDARD-CLASS STANDARD-CLASS> ... [6]     CCL::PRECEDENCE-LIST: (#<STANDARD-CLASS CCL::SIMPLE-FILE-ERROR>                        #<STANDARD-CLASS SIMPLE-CONDITION>                        #<STANDARD-CLASS FILE-ERROR> #<STANDARD-CLASS ERROR>                        #<STANDARD-CLASS SERIOUS-CONDITION> ...) ... [8]     CCL::DIRECT-SUPERCLASSES: (#<STANDARD-CLASS SIMPLE-CONDITION>                            #<STANDARD-CLASS FILE-ERROR>) ... 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: