Jaki jest dobry sposób obsługi wyjątków podczas próby odczytania pliku w Pythonie?

86

Chcę przeczytać plik .csv w Pythonie.

  • Nie wiem, czy plik istnieje.
  • Moje obecne rozwiązanie znajduje się poniżej. Wydaje mi się to niechlujne, ponieważ dwa oddzielne testy wyjątków są niezgrabnie zestawione.

Czy jest na to ładniejszy sposób?

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
Charles Holbrow
źródło
Jeśli nieistniejący plik nie jest przypadkiem błędu, ale prawdopodobną okolicznością, należy sprawdzić i zająć się jego brakiem / nieczytelnością przed (i dodatkowo do) tego, tryże warto. Można to zrobić odpowiednio za pomocą os.path.exists(file)i os.access(file, os.R_OK). Taka kontrola nigdy nie może być wolna od sytuacji wyścigu, ale znikające pliki rzadko są normalną okolicznością;)
stefanct
2
Odpowiedzi na to pytanie prawdopodobnie powinny zostać zaktualizowane, aby uwzględnić użycie pathlibmodułu, co znacznie ułatwia ten problem i prawdopodobnie powinno być standardową praktyką Pythona (zwłaszcza, że ​​został również przeniesiony do wersji 2.7).
Rick wspiera Monikę
podczas gdy to łapie IOError, nie przechwytuje, csv.Errorponieważ plik nie jest w formacie CSV, kiedy Dialect.strict=Truelub Errordla innych błędów (zgodnie z dokumentacją pakietu CSV), więc zewnętrzna próba lub po prostu sprawdzenie pliku istnieje, wtedy wewnętrzna próba dla wyjątków CSV jest prawdopodobnie dobra odpowiedź.
różowy spikyhairman
@pinkspikyhairman Tak, w twoim programie obsługi oprócz musisz zdecydować, które typy błędów chcesz obsłużyć. Zobacz tutaj, jak radzić sobie z wieloma określonymi typami błędów: stackoverflow.com/questions/6470428/ ...
Charles Holbrow

Odpowiedzi:

52

Chyba źle zrozumiałem, o co pytano. Czytając ponownie, wygląda na to, że odpowiedź Tima jest tym, czego chcesz. Dodam jednak tylko jedno: jeśli chcesz wyłapać wyjątek od open, to openmusi być zapakowany w plik try. Jeśli wywołanie openznajduje się w nagłówku a with, to withmusi znajdować się w a, tryaby złapać wyjątek. Nie da się tego obejść.

Więc odpowiedź brzmi: „Sposób Tima” lub „Nie, robisz to poprawnie”.


Poprzednia nieprzydatna odpowiedź, do której odnoszą się wszystkie komentarze:

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

jscs
źródło
23
To, że plik istnieje, nie oznacza, że ​​możesz go przeczytać!
Gabe
3
To nie jest idealne, ponieważ istnieje możliwość, że plik zostanie usunięty (np. Przez inny proces) między sprawdzeniem, czy istnieje, a próbą otwarcia.
Liquid_Fire
Może źle rozumiem pytanie. W rzeczywistości myślę, że zdecydowanie tak.
jscs
1
Możliwe jest również, że fNamemoże to być nazwa pliku, którego nawet jeśli się trzyma, nie można go otworzyć z jakiegokolwiek powodu - na przykład, jeśli jest to katalog lub nie ma uprawnień pozwalających na odczytanie go przez proces wykonawczy.
intuicja
4
Metoda „jeśli istnieje (plik): otwórz (plik)” może się nie powieść, ponieważ plik może zostać usunięty po sprawdzeniu, czy istnieje, ale przed jego otwarciem. Lub może być zablokowany, nie mieć uprawnień do odczytu lub może to być obiekt, którego nie możesz odczytać (na przykład katalog), lub może być zarchiwizowany na taśmie, a taśma jest niedostępna lub może wystąpić błąd dysku próba otwarcia pliku lub ...
Gabe
64

Co powiesz na to:

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
Tim Pietzcker
źródło
10
Jedynym problemem jest to, że plik jest otwierany poza withblokiem. Więc jeśli wystąpi wyjątek między tryblokiem zawierającym wywołanie opena withinstrukcją, plik nie zostanie zamknięty. W tym przypadku, gdy sprawy są bardzo proste, nie jest to oczywisty problem, ale nadal może stanowić zagrożenie podczas refaktoryzacji lub modyfikowania kodu w inny sposób. Biorąc to pod uwagę, nie sądzę, aby był lepszy sposób na zrobienie tego (inny niż oryginalna wersja).
intuicja
2
@intuited: Zgadza się. W rzeczywistości ostateczna odpowiedź na PO jest prawdopodobnie taka: Nie, sposób, w jaki to zrobiłeś, jest właściwy.
jscs
1
FileNotFoundError.mro() jest [<class 'FileNotFoundError'>, <class 'OSError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>]i IOError.mro()jest [<class 'OSError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>]. Co powiesz na użycie albo OSErroralbo Exceptionzamiast tego? ``
hotohoto
1
@hotohoto: Dobry pomysł. Nie jestem pewien - być może hierarchia wyjątków zmieniła się pod tym względem od 2011 roku, ale tak czy inaczej twoja sugestia jest bardziej obszerna.
Tim Pietzcker
16

Oto przykład odczytu / zapisu. Instrukcje with zapewniają, że instrukcja close () zostanie wywołana przez obiekt file niezależnie od tego, czy zostanie zgłoszony wyjątek. 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
źródło
W tym przypadku IOError jest oczywiste, ale kiedy wystąpi ogólny wyjątek z punktu widzenia pokrycia kodu. Jak utworzyć przypadek testowy, aby wygenerować ogólny wyjątek.
Mian Asbat Ahmad
0
fname = 'filenotfound.txt'
try:
    f = open(fname, 'rb')
except FileNotFoundError:
    print("file {} does not exist".format(fname))

file filenotfound.txt does not exist

wyjątek FileNotFoundError Wywoływany, gdy żądany jest plik lub katalog, ale nie istnieje. Odpowiada errno ENOENT.

https://docs.python.org/3/library/exceptions.html
Ten wyjątek nie istnieje w Pythonie 2.

Lou Pendley
źródło
1
Chociaż ten kod może odpowiedzieć na pytanie, dostarczenie dodatkowego kontekstu dotyczącego tego, jak i / lub dlaczego rozwiązuje problem, poprawiłoby długoterminową wartość odpowiedzi.
Kaczor Donald
-12

Dodawanie do przykładu @ Josha;

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)

W ten sposób możesz spróbować otworzyć plik, ale jeśli nie istnieje (jeśli wywołuje IOError), powiadom użytkownika!

Zac Brown
źródło
Nie widzę problemu. Gdyby była to niepoprawna składnia, po wykonaniu wywołałoby błąd składni!
Zac Brown
7
Nie jest to błąd składniowy, ale bool(IOError)jest po prostu Truei ifnie wychwytuje żadnego wyjątku.
8
>>> if IOError: print "That's not an exception handler"
jscs
3
@Josh Caswell ma rację. IOError zwraca wartość True. docs.python.org/2.4/lib/truth.html
hecvd,