尝试在python中读取文件时,处理异常的好方法是什么?

Cha*_*row 57 file-io exception python-2.7

我想在python中读取.csv文件.

  • 我不知道文件是否存在.
  • 我目前的解决方案如下.对我来说这感觉很草率,因为两个独立的异常测试很难并列.

有更漂亮的方法吗?

import csv    
fName = "aFile.csv"

try:
    with open(fName, 'rb') as f:
        reader = csv.reader(f)
        for row in reader:
            pass #do stuff here

except IOError:
    print "Could not read file:", fName
Run Code Online (Sandbox Code Playgroud)

Tim*_*ker 44

这个怎么样:

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
Run Code Online (Sandbox Code Playgroud)

  • 唯一的问题是文件在`with`块之外打开.因此,如果在包含对`open`的调用的`try`块和`with`语句之间发生异常,则文件不会被关闭.在这种情况下,事情非常简单,这不是一个明显的问题,但在重构或修改代码时仍然会带来危险.话虽这么说,我认为没有更好的方法来做到这一点(原始版本除外). (7认同)
  • @intuited:是的。实际上,对OP的最终答案可能只是:不,您的操作方式是正确的方法。 (2认同)

Jos*_*ell 36

我想我误解了被问到的内容.重新阅读,看起来Tim的答案就是你想要的.但是,我只想添加它:如果你想从中捕获异常open,那么open必须将其包含在try.如果调用open是在a的标题中with,则with必须在a try中捕获异常.没有办法解决这个问题.

所以答案是:"蒂姆的方式"或"不,你正确地做到了.".


以前所有评论所指的无益答案:

import os

if os.path.exists(fName):
   with open(fName, 'rb') as f:
       try:
           # do stuff
       except : # whatever reader errors you care about
           # handle error
Run Code Online (Sandbox Code Playgroud)

  • 仅仅因为文件存在并不意味着你可以阅读它! (13认同)
  • 这不是完美的,因为在检查文件存在和尝试将其打开之间可能会删除该文件(例如,通过另一个进程)。 (2认同)
  • "if exists(file):open(file)"方法可能会失败,因为在检查文件是否存在之后但在打开文件之前可以删除该文件.或者它可能被锁定,或者没有读取权限,或者是您无法读取的某种类型的对象(如目录),或者是在磁带上存档而磁带不可用,或者可能存在磁盘错误试图打开文件,或者...... (2认同)

edW*_*edW 11

这是一个读/写示例.with语句确保文件对象将调用close()语句,无论是否抛出异常. 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"
Run Code Online (Sandbox Code Playgroud)


mon*_*mon 7

在Python 3中,IOError是OSError的别名。要验证,请运行代码:

IOError is OSError
---
True
Run Code Online (Sandbox Code Playgroud)

OSError是文件I/O异常的父类。

      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
Run Code Online (Sandbox Code Playgroud)
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]
Run Code Online (Sandbox Code Playgroud)

因此,如果需要详细信息,请捕获 OSError 并检查确切的类。

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'
Run Code Online (Sandbox Code Playgroud)


ate*_*evm 6

我更新了 Tim Pietzcker 答案,因为它使用不再维护的 Python 2。

我尝试先编辑答案,但我得到:编辑队列已满,所以我不能。

import errno
import sys
import os
import csv

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

Run Code Online (Sandbox Code Playgroud)

我还做了一些小改进:

该代码是独立的。

您应该检查异常的errno编号,这有助于您缩小错误范围

您应该将错误和日志消息写入sys.stderr而不是写入sys.stdout(默认打印),因为这样您就可以将错误消息重定向到不同的文件中。

您应该返回一个非零退出代码(此处记录),如果您想让您的 python 代码在 Unix 环境(例如 shell 脚本)中可用,那么这是必须的:

#!/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
Run Code Online (Sandbox Code Playgroud)


Ben*_*ade 5

如何在异常中添加一个“else”子句并将“with”语句放在“else”部分?像这样:

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
Run Code Online (Sandbox Code Playgroud)

代替 sys.exit(),您可以放置​​ 'raise' 并将错误升级到链中。从顶级错误处理程序获取有关错误的系统信息可能会更好。