开发者

What is a good way to handle exceptions when trying to read a file in python?

I want to read a .csv file in python.

  • I don't know if the file exists.
  • My current solution is below. It feels sloppy to me because the two separate exception tests are awkwardly juxtaposed.

Is there a prettier way to do it?

import csv    
fName = "aFile.csv"

try:
    with open(fN开发者_运维知识库ame, 'r') as f:
        reader = csv.reader(f)
        for row in reader:
            pass #do stuff here
    
except IOError:
    print "Could not read file:", fName


How about this:

try:
    f = open(fname, 'rb')
except OSError:
    print "Could not open/read file:", fname
    sys.exit()

with f:
    reader = csv.reader(f)
    for row in reader:
        pass #do stuff here


Here is a read/write example. The with statements insure the close() statement will be called by the file object regardless of whether an exception is thrown. http://effbot.org/zone/python-with-statement.htm

import sys

fIn = 'symbolsIn.csv'
fOut = 'symbolsOut.csv'

try:
   with open(fIn, 'r') as f:
      file_content = f.read()
      print "read file " + fIn
   if not file_content:
      print "no data in file " + fIn
      file_content = "name,phone,address\n"
   with open(fOut, 'w') as dest:
      dest.write(file_content)
      print "wrote file " + fOut
except IOError as e:
   print "I/O error({0}): {1}".format(e.errno, e.strerror)
except: #handle other exceptions such as attribute errors
   print "Unexpected error:", sys.exc_info()[0]
print "done"


How about adding an "else" clause to the exception and putting the "with" statement in the "else" section? Like this:

try:
  f = open(fname, 'rb')
except FileNotFoundError:
    print(f"File {fname} not found.  Aborting")
    sys.exit(1)
except OSError:
    print(f"OS error occurred trying to open {fname}")
    sys.exit(1)
except Exception as err:
    print(f"Unexpected error opening {fname} is",repr(err))
    sys.exit(1)  # or replace this with "raise" ?
else:
  with f:
    reader = csv.reader(f)
    for row in reader:
        pass #do stuff here

Instead of sys.exit(), you could put 'raise' and escalate the error up the chain. It might be better to get system info about the error from the top level error handler.


I updated Tim Pietzcker answer, because it uses Python 2 which is not maintained anymore.

I tried to edit the answer first, but I got: edit queue is full, so I could not.

import errno

fname = "no_such_a_file.txt"

try:
    f = open(fname, 'rb')
except OSError as e:
    if e.errno == errno.ENOENT:
        print(
            f"No such a file or directory (errno: { e.errno }):",
            fname, file=sys.stderr
        )
    else:
        # for other OS errno codes you may want to write
        # your more specific error messages which
        print(
            f"Cannot oppen file (errno: { e.errno } ):",
            fname,
            file=sys.stderr
        )
    sys.exit(os.EX_OSFILE)

with f:
    reader = csv.reader(f)
    for row in reader:
        pass #do stuff here

I also made some minor improvements:

The code is self-contained.

You should check the errno number of your exception, which helps you narrow down the error

You should write error and log messages into sys.stderr and not into sys.stdout (default for print), because then you can redirect your error messages into a different file.

You should return a non-zero exit code (documented here) which is a must if you want to make your python code usable in a Unix environment, such as a shell script:

#!/usr/bin/env bash
set -euo pipefail

if ./read_file.py 2> error.log
then
    echo "do stuff"
else
    exit_code=$?
    echo "file is not readable, exit code: $exit_code" > /dev/stderr
    exit $exit_code
fi


In Python 3, IOError is an alias of OSError. To verify, run the code:

IOError is OSError
---
True
  • PEP 3151 -- Reworking the OS and IO exception hierarchy

OSError is the parent class of the file I/O exceptions.

  • Exception hierarchy
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
OSError.__subclasses__()
---
[ConnectionError,
 BlockingIOError,
 ChildProcessError,
 FileExistsError,
 FileNotFoundError,
 IsADirectoryError,
 NotADirectoryError,
 InterruptedError,
 PermissionError,
 ProcessLookupError,
 TimeoutError,
 io.UnsupportedOperation,
 signal.ItimerError,
 socket.herror,
 socket.gaierror,
 socket.timeout,
 ssl.SSLError,
 shutil.Error,
 shutil.SpecialFileError,
 shutil.ExecError,
 shutil.ReadError,
 urllib.error.URLError,
 gzip.BadGzipFile]

Hence, catch the OSError and check the exact class if detail is requied.

try:
    with open('hoge') as f:
        pass
except OSError as e:
    print(f"{type(e)}: {e}")
---
<class 'FileNotFoundError'>: [Errno 2] No such file or directory: 'hoge'


fname = 'filenotfound.txt'
try:
    f = open(fname, 'rb')
except FileNotFoundError:
    print("file {} does not exist".format(fname))

file filenotfound.txt does not exist

exception FileNotFoundError Raised when a file or directory is requested but doesn’t exist. Corresponds to errno ENOENT.

https://docs.python.org/3/library/exceptions.html
This exception does not exist in Python 2.


Adding to @Josh's example;

fName = [FILE TO OPEN]
if os.path.exists(fName):
    with open(fName, 'rb') as f:
        #add you code to handle the file contents here.
elif IOError:
    print "Unable to open file: "+str(fName)

This way you can attempt to open the file, but if it doesn't exist (if it raises an IOError), alert the user!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜