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!
精彩评论