02 - Wyjątki - czyli obsługa błędów
Uruchom poniższy kod, wpisz nazwę nieistniejącego pliku.
nazwa_pliku = input('Podaj nazwę pliku: ')
plik_odczyt = open(nazwa_pliku, 'rt')
tekst = plik_odczyt.read()
plik_odczyt.close()
print(tekst)
Podaj nazwę pliku: przyyklad_1.txt
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
<ipython-input-5-92dd2df28933> in <module>
1 nazwa_pliku = input('Podaj nazwę pliku: ')
----> 2 plik_odczyt = open(nazwa_pliku, 'rt')
3 tekst = plik_odczyt.read()
4 plik_odczyt.close()
5 print(tekst)
FileNotFoundError: [Errno 2] No such file or directory: 'przyyklad_1.txt'
Z błędami, wynikających z różnych powodów, spotykamy się od początków przygody z programowaniem. W powyższym przykładzie, jeśli użytkownik wpisze nazwę nieistniejącego pliku, prowadzi to przerwania działania programu do i wydrukowania komunikatu o błędzie. Przyjrzyjmy się dokładniej, co się właściwie stało.
W przypadku napotkania na błąd, z którym Python nie potrafi sobie poradzić, tworzy obiekt wyjątku. Jeśli nie zadbamy o to, żeby taki wyjątek został odpowiednio obsłużony, działanie programu zostaje przerwane i zostaje wydrukowany stos wywołań, który wskazuje jaki ciąg poleceń doprowadził do błędu i z jakim rodzajem błędu mamy do czynienia, w powyższym przykładzie jest to FileNotFoundError
. Analizując komunikat, możemy zwykle znaleźć miejsce i przyczynę błędu, ale sam komunikat nie chroni programu przed ,,wykrzaczeniem się’’. Czas zatem nauczyć się, jak taki wyjątek obsłużyć. Uzupełnijmy powyższy kod o konstrukcję try-except
:
nazwa_pliku = input('Podaj nazwę pliku: ')
try:
plik_odczyt = open(nazwa_pliku, 'rt')
tekst = plik_odczyt.read()
plik_odczyt.close()
print(tekst)
# Kod się wykonuje, w przypadku pojawienia się wyjątku
except:
print("Coś poszło nie tak!")
Podaj nazwę pliku: przyyklad_1.txt
Coś poszło nie tak!
Teraz, mimo że plik nie istnieje, program nie przerwał działania, nie widzieliśmy też komunikatów. Zauważ, że wcześniej Python poinformował nas o rodzaju błędu: FileNotFoundError
. Możemy tak zmodyfikować program, aby w przypadku wystąpienia tego właśnie błędu wysłał bardziej precyzyjny komunikat.
nazwa_pliku = input('Podaj nazwę pliku: ')
try:
plik_odczyt = open(nazwa_pliku, 'rt')
tekst = plik_odczyt.read()
plik_odczyt.close()
print(tekst)
# Wyjątek w przypadku braku pliku o podanej nazwie
except FileNotFoundError:
print(f"Nie ma pliku: {nazwa_pliku}")
Podaj nazwę pliku: przyyklad_1.txt
Nie ma pliku: przyyklad_1.txt
Można też przypisać obiekt wyjątku do konkretnej nazwy, np. w celu wyświetlenia informacji o rodzaju błędu. Uzupełnimy kod, przy okazji wprowadzając linię z kodem prowadzącym do powstania innego błędu. Tym razem podaj nazwę istniejącego pliku tekstowego (użyłem pliku z poprzedniej lekcji).
nazwa_pliku = input('Podaj nazwę pliku: ')
try:
plik_odczyt = open(nazwa_pliku, 'rt')
tekst = plik_odczyt.read()
plik_odczyt.close()
# Próbujemy wyświetlić ostatni znak, ale podany jest zły indeks
print(f"Ostatni znak: {tekst[len(tekst)]}")
# Drukowanie zawartości pliku
print(tekst)
# Wyjątek w przypadku braku pliku o podanej nazwie
except FileNotFoundError:
print(f"Nie ma pliku: {nazwa_pliku}")
# Inny wyjątek
except Exception as wyjatek:
print(f"Wystąpił błąd: {wyjatek}")
Podaj nazwę pliku: przyklad_1.txt
Wystąpił błąd: string index out of range
Gdybyśmy chcieli wydrukować nieco bardziej precyzyjne informacje o miejscu wystąpienia błędu, można zaimportować moduł traceback
i dodać odpowiednie polecenie:
import traceback
nazwa_pliku = input('Podaj nazwę pliku: ')
try:
plik_odczyt = open(nazwa_pliku, 'rt')
tekst = plik_odczyt.read()
plik_odczyt.close()
# Próbujemy wyświetlić ostatni znak, ale podany jest zły indeks
print(f"Ostatni znak: {tekst[len(tekst)]}")
# Drukowanie zawartości pliku
print(tekst)
# Wyjątek w przypadku braku pliku o podanej nazwie
except FileNotFoundError:
print(f"Nie ma pliku: {nazwa_pliku}")
# Inny wyjątek
except Exception as wyjatek:
print(f"Wystąpił błąd: {wyjatek}")
# Dodatkowa informacja dotycząca wyjątku:
traceback.print_exc()
Podaj nazwę pliku: przyklad_1.txt
Wystąpił błąd: string index out of range
Traceback (most recent call last):
File "<ipython-input-16-36f670dc8bd0>", line 8, in <module>
print(f"Ostatni znak: {tekst[len(tekst)]}")
IndexError: string index out of range
Dodatkowy blok else
, który możemy dopisać, zawiera kod, który wykonuje się, jeśli nie pojawi się wyjątek w bloku try
. Możemy tam przenieść np. linię odpowiedzialną za wydrukowanie zawartości pliku i dopisać dodatkowy komunikat. Naprawimy też linię kodu generującą błąd indeksu.
import traceback
nazwa_pliku = input('Podaj nazwę pliku: ')
try:
plik_odczyt = open(nazwa_pliku, 'rt')
tekst = plik_odczyt.read()
plik_odczyt.close()
# Teraz prawidłowo wyswietla się ostatni znak
print(f"Ostatni znak: {tekst[len(tekst)-1]}")
# Wyjątek w przypadku braku pliku o podanej nazwie
except FileNotFoundError:
print(f"Nie ma pliku: {nazwa_pliku}")
# Inny wyjątek
except Exception as wyjatek:
print(f"Wystąpił błąd: {wyjatek}")
# Dodatkowa informacja dotycząca wyjątku:
traceback.print_exc()
else:
print(tekst)
print('Wszystko poszło dobrze!')
Podaj nazwę pliku: przyklad_1.txt
Ostatni znak: .
Tekst 1.
Tekst 2.
Tekst 3.Tekst 4.
Wszystko poszło dobrze!
Możemy teraz łatwo napisać kod, który będzie prosił użytkownika do skutku (lub rezygnacji) o podanie prawidłowej nazwy pliku:
while True:
nazwa_pliku = input('Podaj nazwę pliku: ')
try:
plik_odczyt = open(nazwa_pliku, 'rt')
except FileNotFoundError:
print(f"Nie ma pliku: {nazwa_pliku}")
else:
tekst = plik_odczyt.read()
plik_odczyt.close()
break
print(f"Zawartość pliku {nazwa_pliku}:")
print(tekst)
Podaj nazwę pliku: przyyklad_1.txt
Nie ma pliku: przyyklad_1.txt
Podaj nazwę pliku: przyklad_1.txt
Zawartość pliku przyklad_1.txt:
Tekst 1.
Tekst 2.
Tekst 3.Tekst 4.