Domyślnie pliki otwierane są w trybie tekstowym, co pozwala zapisać i odczytać dane w określonym kodowaniu jako stringi. Współcześnie, najczęściej używanym kodowaniem dla tekstu jest standard UTF-8. Trzeba jednak pamiętać, że w trybie tekstowym znaki końca linii dla systemów Unix (\n
) i Windows (\r\n
) różnią się, aczkolwiek przy odczycie tekstowym plików następuje automatyczna konwersja tych znaków na \n
, to przy próbie potraktowania w ten sposób plików z binarnie zapisanymi wartościami nastąpi duże prawdopodobieństwo uszkodzenia i zniekształcenia danych binarnych. W przypadku plików binarnych niemożliwe jest także określenie ich kodowania.
Próba korzystania z plików z zapisem do nich danych napotyka podstawowy problem związany z faktem, iż dane w pliku tekstowym to ciągi znakowe, zatem wszystkie dane innego typu w naszym programie muszą być przekształcane do postaci znakowej, a w drugą stronę konwertowane z tej postaci do właściwego typu danych. W przypadku zagnieżdżających się struktur danych ręczne parsowanie ich staje się skomplikowane i niewygodne, prowadząc najczęściej do błędów.
JSON
Jednym ze sposobów obsługi danych nie będących stringami jest ich serializacja1pobieranie hierarchii danych i automatyczne konwertowanie ich na ciągi liczbowe z wykorzystaniem odpowiednich modułów standardowych. Jednym z nich jest np. json, czyli JavaScript Object Notation. Proces odwrotny, polegający na odczycie danych z pliku we właściwym formacie i przywrócenie ich struktury nazywa się deserializacją.
Przykład stworzenia pliku, zapisania i odczytania używanej złożonej struktury danych, jest zamieszczony poniżej:
import json Dane = (("Anna","Kowalska",1998),("Bartosz","Magłowski",2003)) with open("Dane.json","w", encoding="utf-8") as f: json.dump(Dane,f) f.close() with open("Dane.json","r", encoding="utf-8") as f: odczyDane=json.load(f) for dane in odczyDane: print("Osoba {0} {1} urodziła się w {2} roku.".format(*dane)) f.close()
Jak widać udało nam się stworzyć plik, zapisać do niego dane tekstowe i numeryczne; a następnie przywrócić z pliku te dane dla późniejszej ich obróbki.
Zawartość pliku z powyższego przykładu:
[["Anna", "Kowalska", 1998], ["Bartosz", "Mag\u0142owski", 2003]]
Czy aby wszystko jest ok??
import json Dane = (("Anna","Kowalska",1998),("Bartosz","Magłowski",2003)) with open("Dane.json","w", encoding="utf-8") as f: json.dump(Dane,f) f.close() with open("Dane.json","r", encoding="utf-8") as f: odczyDane=json.load(f) for dane in odczyDane: print("Osoba {0} {1} urodziła się w {2} roku.".format(*dane)) print("Typ zmiennej pierwszej to: ", type(dane[0]).__name__) print("Typ zmiennej drugiej to: ", type(dane[1]).__name__) print("Typ zmiennej trzeciej to: ", type(dane[2]).__name__) print("Rok urodzenia osoby to: {0}, a pożądany typ tej danej to {1} ".format(int(dane[2]),type(int(dane[2])).__name__)) f.close()
Czy zatem serializacja z wykorzystaniem json zabezpiecza nam właściwe typy danych?
Tak, chociaż brak w pliku informacji o typie danych, to Python w sposób utajony dokonał analizy pobieranych danych przypisując je do odtwarzanej struktury, mniej więcej w ten sam sposób w jaki ocenia typ w momencie powoływania do życia i inicjalizowania wartością nowej zmiennej
W efekcie widać zatem, że z naszymi danymi wszystko jest OK.
Zatem spróbujmy napisać program z wykorzystaniem nowo zdobytej wiedzy.
Zadanie 1: Pełnoletność
Napisz program, który po uruchomieniu odczytuje (uprzednio stworzony plik osoby.json z danymi 10 osób, a następnie wyświetla ich Imiona, Nazwiska i wiek każdej z nich, sprawdzając czy jest ona już pełnoletnia, jeśli tak to pisząc na końcu linii znak P, jeśli nie to N.
Co kiszenie (pickle) ogórków ma do plików?
Powyższy przykład modułu z wykorzystaniem standardu JSON pokazuje, że możliwe jest zrzucenie do pliku wartości liczbowych, a następnie ich przywrócenie. Dane przechowywane są w trybie tekstowym, który absolutnie nie jest metodą bezpieczną, ani też nie jest metodą optymalną pod względem zajętości pamięci dyskowej i rozmiaru takiego pliku. Inną metodą jest skorzystanie z modułu pickle
potrafiącego “konserwować” nasze dane 2konserwacja: preservation – to owe pickling: kiszenie danych i dokonywać ich zrzutu do pliku binarnego, oraz odtworzenie ich przy odczycie.
Można zatem rozważyć w jaki sposób to zrobić analizując program poniżej:
import pickle with open("Data.dat", "wb") as plik: d=20 pickle.dump(d, plik) d=11 pickle.dump(d, plik) d=2024 pickle.dump(d, plik) plik.close() with open("Data.dat", "rb") as plik: try: while True: d=pickle.load(plik) print(d) except EOFError: print("Skończyłem czytać zbiór...") plik.close()
Można teraz spróbować otworzyć podgląd tak stworzonego pliku, aby zapoznać się z jego strukturą. Jak widać, struktura ta nie jest zbyt czytelna.
Porównując ten sposób z poprzednim programem uzyskujemy:
import pickle Dane = (("Anna","Kowalska",int(1998)),("Bartosz","Magłowski",2003)) ###zapis obiektu do pliku with open("Data.dat", "wb") as plik: pickle.dump(Dane, plik) ###Odczyt z pliku with open("Data.dat", "rb") as plik: try: while True: odczytaneDane=pickle.load(plik) except EOFError: print("Skończyłem czytać zbiór...") plik.close() for d in odczytaneDane: print("Osoba {0} {1} urodziła się w {2} roku.".format(*d))
Zadanie 2: Pełnoletność i świadomość
Przeredaguj poprzedni program, z wykorzystaniem modułu pickle. Zastanów się nad pros&cons (zaletami i wadami) takiego rozwiązania. Sprawdź wielkość plików danych w obu przypadkach.
Zadanie 2′:
Zwiększ liczbę składowych struktury danych do:
– Identyfikator osoby w standardzie: xxxx-xxxx (jako liczby)
– Imię,
– Nazwisko,
– Data urodzenia RRRR-MM-DD (jako liczby, nie jako string)
– numer telefonu
Zwiększ liczbę elementów takiej “bazy danych” do 100…
Porównaj wielkości plików w pierwszym i drugim sposobie zapisu.
Wyniki rozważań opisz w sprawozdaniu i wgraj je jako plik .pdf (wraz z plikami będącymi listingami programów .py) do Moodle.