Operacje na plikach -------------------------------------- Dotychczas wszystkie programy operowały na danych już wprowadzonych (zmienne, np napisy) lub wylosowanych. Najczęściej jednak dane do programów wczytujemy z plików. Praca z plikiem ma 3 etapy: plik trzeba otworzyć, przeczytac (ew. zapisać) a na końcu zamknąć: .. literalinclude:: programy/plik_wstep.py :language: python :linenos: .. admonition:: Uwaga :class: def Omówiony podczas tego wykładu materiał umożliwia jedynie wczytywanie bądź zapisywanie **plików tekstowych**. Jeżeli chcesz samemu przygotować taki plik, zrób to w Notatniku (na Windows). Niestety nie można w ten sposób wczytywać plików ``.docx`` czy ``.pdf``. Będzie to możliwe po zainstalowaniu dodatkowych bibliotek (modułów) języka Python. Do otwierania pliku w Pythonie służy instrukcja ``open()``, która przyjmuje co najmniej jeden argument: nazwę pliku z ewentualną ścieżką. Drugim (opcjonalnym) argumentem jest litera oznaczająca przeznaczenie otwartego pliku: - ``'r'`` (*read*) - plik otwarto do odczytu - jest to wartość domyślna; innymi słowy: ``open("plik")`` oznacza dokładnie to samo, co ``open("plik",'r')``; w tak otwartym pliku nie uda się nic zapisać - ``'w'`` (*write*) - plik otwarto do zapisu; jeżeli plik już istnieje, jego zawartośc zostanie skasowana - ``'a'`` (*append*) - plik otwarto do zapisu w trybie **dopisywania** .. admonition:: Uwaga :class: def Python działający w przeglądarce nie może wczytywać plików. W tym wykładzie skorzystamy pewnego *tricku*, w którym zmienną napisową (tekst) zamienimy na plik. Umożliwi nam to obiekt ``StringIO`` z modułu ``io``. W przykładach, które uruchamiacie na swoich komputerach poza przeglądarką (np w środowisku PyCharm lub w terminalu), linię ``plik = io.StringIO(napis)`` powinniście zamienić na ``plik = open("nazwa_pliku")``. Plik ten oczywiście musi być nagrany na dysku Waszego komputera a jego zawartość - taka jak w przykładach. Czytanie z pliku """""""""""""""""""" .. raw:: html
Zacznijmy od wczytywania zawartości pliku, co można zrobić na kilka sposobów. Skrypt powyższy przetwarza plik linijka po linijce (pętla w liniach 8 i 9). Ponieważ nie wczytujemy całego pliku na raz do pamięci, w ten sposób możemy przetwarzać nawet bardzo duże pliki. Jeżeli jednak zależy nam na załadowaniu całego pliku, możemy użyć instrukcji ``read()``, jak pokazano poniżej w linii 10: .. raw:: html
Jak widać, instrukcją ``readlines()`` (linia 16) można wczytać wszystkie linie pliku jako listę. Na końcu programu raz jeszcze wczytujemy ten plik linija po linii, korzystając z pętli (linie 20,21). .. admonition:: Uwaga :class: def W podanych tu przykładach, uruchamianych w przeglądarce, niestety nie widać, że Python wczytuje linie wraz z kończącym je *znakiem końca linii*, czyli ``'\n'``. Wykonanie powyższego programu w terminalu (lub w środowisku PyCharm) da wynik jak poniżej: .. code-block:: bash Ala ma kota. Kot ma Alę. Zuza ma psa. ['Ala ma kota.\n', 'Kot ma Alę.\n', 'Zuza ma psa.\n'] Ala ma kota. Kot ma Alę. Zuza ma psa. Jak widać, ponowne wydrukowanie linii powoduje, że pojawiają się też linie puste. Zapobiegamy temu stosując poznaną niedawno metodę ``linia.strip()``. Zapis do pliku """""""""""""""""""" Zapisywanie jest równie proste, jak czytanie; służy do tego instrukcja ``write()``. Poniższy przykład zapisuje dwie kolumny liczb losowych do pliku: .. literalinclude:: programy/zapisz_losowe.py :language: python :linenos: .. admonition:: Uwaga :class: def Otwarte pliki koniecznie należy zamknąć! .. rubric:: A co jeśli nie zamknę pliku? Otwarty w programie plik może powodować problemy. Pliki otwarte do zapisu mogą *"zniknąć"* albo stracić część danych. Może się też okazać, że inne aplikacja nie może odczytać pliku, który pozostaje otwarty. Jeżeli korzystasz z najpopularniejszej (i domyślnej) dystrybucji CPython, to otwarte pliki zostaną zamknięte automatycznie przy końcu programu. Nie każda dystrybucja jednak robi to za nas. Dobrą praktyką jest robienie tego samodzielnie. Instrukcja ``with`` """""""""""""""""""" Choć instrukcja ``with`` wygląda zupełnie inaczej, niż przykłady powyżej, w praktyce robi prawie to samo: otwiera plik, który można np przeczytać: .. code-block:: python with open('plik.txt') as plik: for linia in plik: print linia.strip().split() Dodatkowo instrukcja ``with`` pilnuje, aby zamknąć plik. Robi to nawet wtedy gdy coś pójdzie nie tak, np. nastąpi jakiś wyjątek. .. raw:: html
Powyższy program to typowy przykład przetwarzania plików za pomocą Pythona. Program otwiera plik (linia 7), następnie w pętli biegnie po wszystkich jego liniach (wiersz 8). W wierszu 9 każda z linii jest przetwarzana: obcinane są *białe znaki* z obydwu końców linii a nastepnie linia dzielona jest na wyrazy. Instrukcja ``with`` gwarantuje nam, że plik zostanie poprawnie zamknięty po zakończeniu przetwarzania. Często jednak potrzeba *jednocześnie* przetwarzać dwa pliki; dobrym przykładem jest sytuacja, w której czytamy dane z jednego pliku, analizujemy je, a wyniki na bieżąco nagrywamy do drugiego pliku. Poniższy przykład pokazuje, jak zrobić to stosując instrukcję ``with``: .. literalinclude:: programy/grep.py :language: python :linenos: Powyższy program naśladuje Linuxowy program ``grep``. Pobiera z linii poleceń nazwę pliku wejściowego, wyrażenie regularne oraz nazwę pliku wyjściowego. W pliku wyjściowym nagrane zostaną tylko te linie, w których znaleziony zostanie podany wzorzec. Przy okazji program podsumowuje materiał, z którym zapoznaliście się już do tej pory: - linia 1 importuje cały moduł ``re``, podczas gdy w linii 2 importowana jest jedynie lista ``argv`` :ref:`(importowanie opisano tutaj)` - w liniach 5-9 stworzona jest zmienna - napis o wielu liniach (fragment tekstu) - w linii 11 :ref:`(deklarujemy funkcję)` o 3 argumentach - w linii 12 *kompilujemy wyrażenie regularne* :ref:`(tutaj)`, które zostanie wykorzystane w tej funkcji - w linii 13 stosujemy instrukcję ``with`` do jednoczesnego otwarcia dwóch plików - linia 14 to pętla po wierszach z pliku - a linia 15 zawiera warunek sprawdzający, czy dana linia pasuje do wyrażenia regularnego - pasujące linie nagrywane są w linii 16 :ref:`(sekcję main opisano tutaj)
` - w linii 18 sprawdzamy, czy powyższy program został uruchomiony, czy jest importowany - w linii 19 zaś liczymy, ile argumentów mu przekazano :ref:`(listę argumentów argv opisano tutaj)
` Podstawowe operacje na plikach i katalogach """""""""""""""""""""""""""""""""""""""""""" Dotychczas zakładaliśmy, że pliki zawsze są tam, gdzie ich szukamy. Co jednak zrobić, kiedy nie wiemy, jaki plik chcemy wczytać? A jak sprawdzić, czy dany plik istnieje? Odpowiedzi na te pytania należy szukać w modułach standardowej biblioteki Pythona: - ``os.path.isfile(ścieżka)`` - sprawdza, czy podany plik wraz z podaną ścieżką istnieje i czy naprawdę *jest plikiem*; zwraca prawdę lub fałsz - ``os.path.exists(ścieżka)`` - sprawdza, czy podana ścieżka istnieje, nie koniecznie musi ona być plikiem - ``os.path.isdir(ścieżka)`` - sprawdza, czy podana ścieżka jest istniejącym katalogiem - ``glob.glob(ścieżka_maska)`` - wyszukuje wszystkie pliki wg podanej maski, np: ``glob.glob("*.gif")`` zwróci listę wszystkich plików z bieżącego katalogu, które kończą się na ``".gif"`` - ``os.getcwd()`` zwraca ścieżkę do bieżącego katalogu, w którym aktualnie wykonywany jest program. Oznacza to na przykład to, że pliki otwarte instrukcją ``open("plik",'w')`` pojawią się w tym właśnie katalogu - ``os.mkdir('folder')`` - zakłada nowy katalog (folder) w katalogu bieżącym .. admonition:: Praca domowa Poniższy program drukuje na ekranie zawartość pliku linijka po linijce. Zmodyfikuj go tak, aby obliczał **wartość średnią** z liczb w drugiej kolumnie. Pamiętaj przy tym o: - sprawdzeniu, czy linia aby nie jest pusta - obcięciu białych znaków z końców linii przed podzieleniem jej na wyrazy - tym, że każdy wyraz jest zmienną napisową, którą trzeba zamienić na liczbę .. raw:: html