https://docs.python.org/3/library/functions.html#open https://docs.python.org/3/library/exceptions.html
Pliki
Otwieranie pliku
Aby móc zapisywać dane do pliku, plik należy najpierw otworzyć za pomocą funkcji open(nazwa_pliki, tryb_dostępu)
. Po otwarciu pliku otrzymujemy uchwyt do pliku, czyli wskaźnik na dane miejsce w pliku, które pozwala na odczytywanie lub zapisywanie do niego. Uchwyt do pliku można traktować jako kursor. Każda operacja przesuwa ten wskaźnik o tyle znaków ile zostało użytych w poleceniu. Tryb dostępu określa w jaki sposób będzie zachowywał się plik i w którym miejscu zostanie umieszczony uchwyt:
r
(read only) - plik tylko do odczytu. Uchwyt jest umieszczany na początku pliku. Jest to domyślny tryb dostępuw
(write only) - plik tylko do zapisu. Jeżeli plik wcześniej nie istniał to zostanie utworzony. Jeżeli plik wcześniej istniał to jego zawartość zostaje usunięta. Uchwyt jest umieszczany na początku pliku.a
(append only) - plik do dopisu. Nowe dane będą umieszczone na końcu pliku. Jeżeli plik wcześniej nie istniał to zostanie utworzony.
Jeżeli do nazwy trybu dodamy +
, to pozwoli on na wszystkie operacje (np. tryb r+ pozwoli również na zapis)
Dodatkowo plik można otworzyć jako:
t
- plik tekstowy (domyślnie) - tekst czytelny dla człowiekab
- plik binarny - dane w formie zer i jedynek czytelne tylko dla innych programów
Tryby otwarcia pliku można łączyć, tworząc różne kombinacje, na przykład:
rt
- plik tekstowy do odczytur+b
- plik binarny do odczytu i zapisuwt
- plik tekstowy do zapisu
Należy pamiętać aby po zakończeniu obsługi pliku zamknąć go metodą close()
.
file = open("database.txt", "rt")
file.close()
Uwaga! Należy unikać otwarcia kilku uchwytów na raz, ponieważ może to spowodować zapisywanie jednocześnie kilku rzeczy w tym samym miejscu
Odczytywanie danych z pliku
Metoda read(size)
służy do odczytywania danych z pliku. Domyślnie odczytuje cały plik, ale można podać jako jej argument liczbę znaków którą ma odczytać. Aby odczytać tylko jedną linię tekstu można użyć metody readline(size)
. Tutaj również można podać maksymalną ilość znaków która ma być odczytana. Z kolei metoda readlines()
odczyta wszystkie linie tekstu i zapisze je w liście.
Zapisywanie danych do pliku
Aby zapisać dane do pliku należy użyć metody write(x)
, gdzie x jest danymi które dopisujemy do pliku.
Uwaga! Ze względu na wewnętrzny mechanizm odczytu, po odczytaniu tylko części pliku i przejściu do zapisu, uchwyt i tak przeniesie się na koniec dlatego zazwyczaj najlepiej jest ręcznie pozycjonować kursor przez zapisem.
file = open("numbers.txt", "r+t")
old_data = file.readline()
file.write(new_data)
file.close()
Usuwanie danych z pliku
Aby usunąć dane, należy użyć metody truncate()
. Metoda ta działa tak samo jak read(x)
- argument x
określa ilość znaków do usunięcia, a w wypadku pozostawienia pustego pola usuwany jest cały plik.
Przesuwanie uchwytu
Do przesuwania uchwytu w pliku bez zmieniania jego zawartości służy metoda seek(start, przesunięcie)
. Jako start należy podać z którego miejsca chcemy zacząć przesunięcie, a następnie podać o ile chcemy przesunąć uchwyt. Metoda ta nie pozwala jednak przesuwać się w tył pliku.
Możemy wybrać jeden z trzech punktów początkowych:
- 0 - początek pliku
- 1 - obecna pozycja w pliku
- 2 - koniec pliku
Aby przesunąć uchwyt na początek pliku wystarczy użyć metody seek(0,0)
, a na koniec seek(2,0)
.
Do odczytania bieżącej pozycji uchwytu służy metoda tell()
.
Aby odczytać liczbę z pliku a następnie powiększyć ją o jeden i zapisać znowu, można się posłużyć następującym kodem:
file = open("secret_number.txt", "r+t")
old_number = int(file.read())
file.seek(0,0)
file.truncate()
file.write(str(old_number+1))
file.close()
Obsługa wyjątków
W normalnej sytuacji jeżeli w programie wystąpi błąd, oznacza to że nastąpiła nieprzewidziana sytuacja, i dalsze kontynuowanie działania programu nie ma sensu. Program natychmiast zakańcza działanie i zwraca informację o błędzie. Czasem jednak błędy muszą być wzięte pod uwagę jako normalne zdarzenie w czasie działania, zwłaszcza jeżeli program ma wchodzić w interakcję z innymi programami, plikami, siecią czy użytkownikiem. W takim wypadku należy zaimplementować prawidłowe przywrócenie programu do działania po wystąpieniu błędu. Aby wystąpienie błędu nie zatrzymało działania programu, należy zastosować mechanizm wyjątków (exception). W momencie wystąpienia błędu program uruchamia specjalną procedurę, zwaną wyjątkiem, która pozwala przywrócić normalne działanie. Aby zastosować ten mechanizm, należy kod mogący “rzucić” wyjątek zamknąć w bloku try/except. Sekcja try
uruchamia program, ale w razie jego awarii nie zatrzymuje się, tylko przechodzi do bloku except
, gdzie uruchamia odpowiednią procedurę.
try:
print(x)
except:
print("Wystąpił błąd w funkcji print")
# dalszy ciąg programu wykonuje się bez zakłóceń
Wyjątki mogą mieć własne nazwy, co pozwala na zareagowanie na różne rodzaje błędów które mogły wystąpić. Blok ‘except’ z podaną nazwą wyjątku zareaguje tylko na ten konkretny błąd, a bez podania nazwy wyjątku złapie wszystkie które pozostały. Jeżeli wyjątek zostanie złapany, to jest usuwany. Dlatego blok except
bez nazwy powinien być na końcu Dodatkowo pusty except
powinien być rzadkością, ponieważ wszystkie możliwe błędy powinny być znane przed uruchomieniem programu.
try:
print(x)
except NameError:
print("brak zmiennej o podanej nazwie")
except:
print("nieznana awaria w funkcji print")
Dodatkowo do bloku try/except można dodać bloki else
i finally
, które zostaną wywołane odpowiednio jeżeli błąd nie wystąpił, oraz niezależnie od tego czy wystąpił czy nie.
try -> except/else -> finally
try:
f = open("filename")
except:
print("File opening error")
else:
f.close()
finally:
print("file operations completed")
Aby rzucić wyjątek należy użyć polecenia raise
i jednej z dostępnych klas wyjątków, opisującej błąd. Ogólnym typem wyjątku jest klasa Exception
, istnieją także klasy do opisu innych błędów, np.: ValueError
, TypeError
, ZeroDivisionError
, FileNotFoundError
, NameError
, KeyError
, IndexError
itp. Można także stworzyć własną klasę wyjątku.
def divide_object(object, number):
if number == 0:
raise ZeroDivisionError
else:
return object/number
Zabezpieczanie zasobu
Aby otworzyć jakiś zasób (plik, port sieciowy itp.) w sposób gwarantujący bezpieczne rozwiązanie błędów i automatyczne zamknięcie zasobu, można użyć słowa kluczowego with
. Tworzy on osobny blok kodu w którym możemy korzystać z zasobu, a po jego opuszczeniu zasób jest zamykany. Zapobiega to błędom związanym z nieprawidłowym zamknięciem zasobu jeżeli w czasie obsługi pliku wystąpi błąd. Jeżeli w bloku with
wystąpi wyjątek, nadal powinno się go obsłużyć. Dodatkowo można użyć słowa kluczowego as
, by nadać własną nazwę zasobowi.
with open("database.txt", "r+") as data:
data.write(...)
Na końcu bloku with
nie ma potrzeby zamykać pliku gdyż dzieje się to automatycznie.
Ćwiczenia:
- Stwórz w notatniku plik z kilkoma imionami, możesz nazwać go
imiona.txt
. Następnie stwórz program który go otworzy i zamknie. - Odczytaj z pliku kilka pierwszych imion i wypisz je na ekranie.
- Zapisz w pliku jakieś nowe imię.
- Usuń ostatnie imię z pliku.
- Przesuń kursor do środka pliku i zapisz tam nowe imię.
- Obsłuż wyjątek który mógłby wystąpić gdyby plik który chcemy otworzyć nie istniał.
Zadania
Zaszyfrowany plik
- Napisz program do tworzenia zaszyfrowanych wiadomości. Użytkownik powinien podać zwykłą wiadomość tekstową, a program powinien ją zaszyfrować w dowolny sposób i zapisać do pliku. Metoda szyfrowania może być dowolna (np. wstawianie co drugi znak losowego znaku tekstowego).
- Napisz program deszyfrujący wiadomości z poprzedniego podpunktu.
- Dodaj do obu programów procedury obsługi wyjątków.
Zajezdnia
- Stwórz własne klasy które będą reprezentować zajezdnię tramwajową oraz tramwaje. Przeciąż metody
__add__
i__sub__
aby móc dodawać i zabierać tramwaje z zajezdni. Opis przeciążania dodawania i odejmowania znajduje się tutaj. - Dodaj obsługę wyjątków która zapewni że nie będzie można wykonać niemożliwych operacji matematycznych.
Projekt
Do gry możemy dodac system umożliwiający zapamiętanie np. najwyższej ilości punktów zdobytych przez gracza.
- Dodaj system który będzie przyznawał punkty na zakończenie gry, np. za zcas przejścia, ilośc przedmiotów, zachowane hp.
- Stwórz plik w którym będzie przechowywana rekordowa liczba punktów zdobyta w grze. Po zakończeniu gry sprawdź czy nowa ilośc punktów nie przewyższyła starej i zapisz ją.
- Stwórz system radzenia sobie z wyjątkami, np. w wypadku braku pliku.