Как лучше обрабатывать исключения при попытке прочитать файл в Python?

86

Я хочу прочитать файл .csv на Python.

  • Я не знаю, существует ли файл.
  • Мое текущее решение ниже. Мне это кажется неряшливым, потому что два отдельных теста исключения неудобно сопоставлены.

Есть способ сделать это красивее?

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
Чарльз Холброу
источник
Если несуществующий файл является не ошибкой, а вероятным обстоятельством, тогда проверка и обработка его отсутствия / нечитаемости явно до (и в дополнение к) tryможет стоить того. Это можно сделать с помощью os.path.exists(file)и os.access(file, os.R_OK)соответственно. Такая проверка никогда не может быть свободна от состояния гонки, но исчезновение файлов редко является нормальным явлением;)
stefanct
2
Ответы на этот вопрос, вероятно, следует обновить, чтобы включить использование pathlibмодуля, который значительно упрощает эту проблему и, вероятно, должен быть стандартной практикой Python (тем более, что он также был перенесен на 2.7).
Рик поддерживает Монику
пока этот улавливает IOError, он не улавливает csv.Errorиз-за того, что файл не является форматом CSV, когда Dialect.strict=Trueили Errorдля каких-либо других ошибок (в соответствии с документами пакета CSV), поэтому существует внешняя попытка или просто проверка файла, тогда внутренняя попытка для исключений CSV наверное правильный ответ.
Pink Spikyhairman
@pinkspikyhairman Да, в обработчике except вам нужно решить, какие типы ошибок вы хотите обрабатывать. См. Здесь, как обрабатывать несколько конкретных типов ошибок: stackoverflow.com/questions/6470428/…
Чарльз Холброу

Ответы:

52

Думаю, я неправильно понял, о чем спрашивали. Перечитывая, похоже, что ответ Тима - это то, что вы хотите. Однако позвольте мне просто добавить следующее: если вы хотите перехватить исключение из 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

ао
источник
23
То, что файл существует, не означает, что вы можете его прочитать!
Гейб
3
Это не идеально, потому что возможно, что файл будет удален (например, другим процессом) между проверкой его существования и попыткой его открытия.
Liquid_Fire
Может я неправильно понял вопрос. На самом деле, я думаю, что это так.
ао
1
Также возможно, что это fNameможет быть имя какого-то файла, который, даже если он застрял, не может быть открыт по какой-либо причине - например, если это каталог или нет разрешений, позволяющих его читать выполняющему процессу.
интуитивно понятен
4
Метод «если существует (файл): открыть (файл)» может завершиться ошибкой, поскольку файл может быть удален после того, как вы проверите, существует ли он, но до того, как вы его откроете. Или он может быть заблокирован, или у него нет разрешения на чтение, или это может быть какой-то объект, который вы не можете прочитать (например, каталог), или он может быть заархивирован на ленте, и лента недоступна, или может быть ошибка диска пытаюсь открыть файл или ...
Гейб
64

Как насчет этого:

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
Тим Пицкер
источник
10
Единственная проблема в том, что файл открывается вне withблока. Поэтому, если между tryблоком, содержащим вызов, openи withоператором возникает исключение , файл не закрывается. В этом случае, когда все очень просто, это не очевидная проблема, но все же может представлять опасность при рефакторинге или ином изменении кода. При этом я не думаю, что есть лучший способ сделать это (кроме оригинальной версии).
интуитивно понятен
2
@intuited: Верно. Фактически, окончательный ответ на OP, вероятно, будет справедливым: нет, вы сделали это правильно.
ао
1
FileNotFoundError.mro() есть [<class 'FileNotFoundError'>, <class 'OSError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>]и IOError.mro()есть [<class 'OSError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>]. Как насчет использования одного OSErrorили Exceptionвместо него? ``
hotohoto
1
@hotohoto: Хорошая идея. Я не уверен - возможно, иерархия исключений изменилась в этом отношении с 2011 года, но в любом случае ваше предложение более всеобъемлющее.
Тим Пицкер
16

Вот пример чтения / записи. Операторы 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"
edW
источник
В этом случае ошибка IOError очевидна, но когда произойдет общее исключение с точки зрения покрытия кода. Как я могу создать тестовый пример для генерации общего исключения.
Миан Асбат Ахмад
0
fname = 'filenotfound.txt'
try:
    f = open(fname, 'rb')
except FileNotFoundError:
    print("file {} does not exist".format(fname))

file filenotfound.txt does not exist

исключение FileNotFoundError Возникает, когда файл или каталог запрашиваются, но не существуют. Соответствует ошибке ENOENT.

https://docs.python.org/3/library/exceptions.html
Это исключение не существует в Python 2.

Лу Пендли
источник
1
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, как и / или почему он решает проблему, улучшило бы долгосрочную ценность ответа.
Дональд Дак
-12

Добавление к примеру @Josh;

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)

Таким образом, вы можете попытаться открыть файл, но если он не существует (если он вызывает ошибку IOError), предупредите пользователя!

Зак Браун
источник
Не вижу проблемы. Если это был неправильный синтаксис, при выполнении возникла бы синтаксическая ошибка!
Zac Brown
7
Это не синтаксическая ошибка, bool(IOError)это просто Trueи ifне обнаруживает никаких исключений.
8
>>> if IOError: print "That's not an exception handler"
jscs
3
@ Джош Касвелл прав. IOError оценивается как True. docs.python.org/2.4/lib/truth.html
hecvd,