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ąć:
1# Otwarcie pliku
2plik = open("plik.txt")
3# wczytanie zawartości
4zawartosc = plik.read()
5# zamknięcie pliku
6plik.close()
Uwaga
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, coopen("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
Uwaga
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¶
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:
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).
Uwaga
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:
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:
1from random import random
2
3plik = open("losowe.txt", 'w')
4for i in range(100):
5# plik.write(random()," ",random(),"\n")
6 plik.write("%f %f \n" % (random(),random()))
7plik.close()
Uwaga
Otwarte pliki koniecznie należy zamknąć!
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ć:
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.
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:
1import re
2from sys import argv
3
4
5info = """\ngrep.py: szuka wzorca w pliku
6
7Użycie:
8 python3 grep.py plik_we "wzorzec" plik_wy\n
9"""
10
11def grep(plik_we, wyrazenie, plik_wy):
12 regex = re.compile(wyrazenie)
13 with open(plik_we) as plik_we, open(plik_wy,'w') as plik_wy:
14 for linia in plik_we:
15 if regex.search(linia) :
16 plik_wy.write(linia)
17
18if __name__ == "__main__":
19 if len(argv) != 4 : print(info)
20 else:
21 grep(argv[1], argv[2], argv[3])
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 listaargv(importowanie opisano tutaj)w liniach 5-9 stworzona jest zmienna - napis o wielu liniach (fragment tekstu)
w linii 11 (deklarujemy funkcję) o 3 argumentach
w linii 12 kompilujemy wyrażenie regularne (tutaj), które zostanie wykorzystane w tej funkcji
w linii 13 stosujemy instrukcję
withdo jednoczesnego otwarcia dwóch plikówlinia 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 (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 (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
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ę