In [1]:
# setup
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"

Czytanie i zapis do plików

Proces czytania i zapisu danych do pliku to zadanie złożone. Python jak większość języków programowania pozwala wczytywać dane ze zbiorów zewnętrznych, jak i zapisywać dane do plików. Proces czytania można zrealizowaćć przy pomocy funkcji wbudowanych open(), read() i close() lub - w przypadku danych o ustalonej strukturze - najczęściej tabelarycznej, można skorzystać z gotowych funkcji wspierających ten proces. W pierwszej kolejności przeanalizujemy proces czytania danych przy pomocy fukcji wbudowanych a następnie pokażemy przykład rozwiązań zewnętrznych.

Aby odczytać plik tekstowy należy wykonać trzy polecenia:

  • open() aby nawiazać połączenie z plikiem (nic nie zostaje wczytane)
  • read() aby wczytać całą zawartość pliku do jednej zmiennej jako tekst
  • close() zamknąć plik po zakończeniu czytania

Dodatkowo proces czytania lub zapisu wymaga znalezienia właściwego pliku, lub - w przypadku odczytu utworzenia nowego pliku.

Jako przykład pokażemy wczytanie niewielkiego pliku tekstowego znajdującego się w tym samym katalogu co nasz skrypt.

In [2]:
# -*- coding: utf -8  -*-
f = open("plik.txt")
zawartosc = f.read()
f.close()
zawartosc
Out[2]:
'To jest zawartosc pliku\nLinia 2\nLinia 3\n\nPowyzej jest pusta linia\n'

Funkcja open() nawiązuje połączenie z plikiem, ale nie wczytuje danych. Przypisuje jedynie do obiektu f wskaźnik do pliku. Wynika to z faktu że zbiór danych może być bardzo duży i programista powinien zachować kontrolę nad jego wczytywaniem. Funkcja read() wczytuje całą zawartość ze źródła f (pliku) do zmiennej zawartość. Następnie źródło zostaje zamknięte.
Znak "\n" znajdujący się w obrębie zmiennej oznacza znak końca linii, aby rozbić taką linię na osobne należy zastosować funkcję split(), wskazując znak użyty do rozbicia.

In [3]:
zawartosc.split("\n")
Out[3]:
['To jest zawartosc pliku',
 'Linia 2',
 'Linia 3',
 '',
 'Powyzej jest pusta linia',
 '']

W przypadku dużych plików lepiej zastosować procedurę czytania linia po linii. Czytanie linia po linii jest też operacją stosowaną, gdy chcemy odczytać z pliku konkretne linie:

In [4]:
f = open("plik.txt")
f.readline()
f.readline()
f.readline()
f.close()
Out[4]:
'To jest zawartosc pliku\n'
Out[4]:
'Linia 2\n'
Out[4]:
'Linia 3\n'

W ten sposób możemy kontrolować które linie zostaną zapisane do dalszego przetwarzania.

Funkcja open() domyślnie otwiera plik tekstowy, do odczytu, tym samym wywołanie funkcji open() jest równoważne open(file,"rt"). Można również otwierać pliki w innych trybach, na przykład w trybie binarnym ("b") lub w trybie do zapisu ("w"), do zapisu z dołączaniem ("a"), czy w trybie tworzenia nowego pliku ("x").

Czytanie i przetwarzanie plików tabelarycznych

Ponieważ python wczytuje dane w postaci pojedynczej zmiennej tekstowej, jeżeli wczytane dane zamierzamy poddać obliczeniom, należy je odpowiednio przekształcić, najczęściej do postaci tabelarycznej oraz zmodyfikować typy danych z tekstowych do numerycznych. Do tego celu służą poznane już funkcje split() oraz funkcje konwersji zmiennych. Procedura czytania danych jest następująca:

  1. W pierwszysm kroku nawiązywane jest połączenie z plikiem (zawartość zostaje wyświetlona)
  2. Dane zostają podzielone na listę łańcuchów tekstowych, względem znaku "\n" a następnie każda linia zostaje podzielona na listy wewnętrzne na podstawie przecinka (separator w csv). Wynik zostaje wyświetlony jako lista zagnieżdżona
  3. Połączenie zosaje zamknięte
  4. Z listy zostaje usunięta 1 linia (nagłówek)
  5. Z listy zostaje usunięta ostatnia pusta linia (z reguły występuje w plikach tekstowych)
In [5]:
f = open("tab.csv") #1
dane = f.read() #
dane
dane = dane.split('\n') #2 podziel po liniach
dane = [l.split(',') for l in dane] #2 podziel po wierszach
dane
f.close() #3
header = dane.pop(0) #4 nagłówek
dane.pop() #5 usunięcie ostatniego, pustego wiersza
Out[5]:
'Ulica, Nr_domu, kod, miejscowosc\nPasikonika, 20, 81-098, Lubowo\nBiedronki, 15, 81-789, Bobowo\nZuczka, 10, 61-874, Rabowo \nGasienicy, 33, 41-789, Warbowo\n'
Out[5]:
[['Ulica', ' Nr_domu', ' kod', ' miejscowosc'],
 ['Pasikonika', ' 20', ' 81-098', ' Lubowo'],
 ['Biedronki', ' 15', ' 81-789', ' Bobowo'],
 ['Zuczka', ' 10', ' 61-874', ' Rabowo '],
 ['Gasienicy', ' 33', ' 41-789', ' Warbowo'],
 ['']]
Out[5]:
['']

Dane są przechowywane w postaci listy list, gdzie każda lista zagnieżdżona to pojedynczy wiersz składający się z danych różnego typu. Ponieważ operowanie na wierszach zawierających dane różnego typu nie jest wygodne, można przekształcić listę zagnieżdżoną do postaci kolumnowej (dokonać transpozycji) przy pomocy poznanej wcześniej funkcji zip() Dane przekazujemy z * czyli rozwijamy przekazaną listę do postaci: dane[0], dane[1],...,dane[n-1]

In [6]:
dane = zip(*dane)
trans = list(dane)
trans
Out[6]:
[('Pasikonika', 'Biedronki', 'Zuczka', 'Gasienicy'),
 (' 20', ' 15', ' 10', ' 33'),
 (' 81-098', ' 81-789', ' 61-874', ' 41-789'),
 (' Lubowo', ' Bobowo', ' Rabowo ', ' Warbowo')]

w ostatnim kroku możemy przekształcić 2 i 3 linię do typu integer. Użyjemy do tego mapowania funkcji i funkcji anonimowej:

In [7]:
numery = tuple(map(int,trans[1]))
numery
Out[7]:
(20, 15, 10, 33)
In [8]:
kody = tuple(map(lambda x: int(x[:3]+x[4:]), trans[2]))
kody
Out[8]:
(81098, 81789, 61874, 41789)

Funkcja anonimowa pobiera pierwszs dwa znaki [:3] oraz od znaku czwartego do końca [4:] łączy je w jeden ciąg a następnie zamienia na typ integer. Funkcja map mapuje wynik na całą linię z kodami.

Użycie funkcji csv reader

Jeżeli dysponujemy danymi o strukturze tabelarycznej możemy uprościć proces czytania danych oprzez zastosowanie funkcji reader() z modułu csv.

Note: klauzula with

W przypadku odczytu danych dobrze jest zamykać proces czytania w obrębie klauzuli with. Klauzula with pozwala uniknąć problemów w przypadku wystąpienia wyjątku w trkacie wczytywania danych i gwarantuje że po zakończeniu procesu wczytywania połączenie z plikiem zostanie zamknięte. W obrębie kalzuli with powinien się znaleść proces czytania (tworzy generator, nie czyta danych) ora proces zamiany generatora na listę. Jeżeli obie operacje zakończą się sukcesem otrzymamy obiekt z danymi a plik zostanie zamknięty. Jeśli nie obiekt dane nie zostanie utworzony ale połączenie i tak zostanie zamknięte.

In [9]:
import csv
with open('tab.csv') as csvfile:
    reader = csv.reader(csvfile, delimiter=',')
    dane  = list(reader)

dane
Out[9]:
[['Ulica', ' Nr_domu', ' kod', ' miejscowosc'],
 ['Pasikonika', ' 20', ' 81-098', ' Lubowo'],
 ['Biedronki', ' 15', ' 81-789', ' Bobowo'],
 ['Zuczka', ' 10', ' 61-874', ' Rabowo '],
 ['Gasienicy', ' 33', ' 41-789', ' Warbowo']]

Zapis do pliku

Aby zapisać plik tekstowy należy otworzyć go w trybie do zapisu ('w') a następnie zapisać zawartość całego łańcucha funkcją write(). W przypadku gdy chcemy zapisać listę, należy ją wcześniej zamienić na łańcuch tekstowy, gdyż funkcja write() może zapisywać tylko łańcucy tekstowe.

In [10]:
mce = ['styczen', 'luty', 'marzec', 'kwiecien', 'maj', 'czerwiec', 'lipiec', 'sierpien', 'wrzesien', 'pazdziernik', 'listopad', 'grudzien'] 
linia = ",".join(mce)
f = open("wynik.txt",mode='w')
f.write(linia)
f.close()
Out[10]:
96

lub w przypadku listy zapisać wiersz po wierszu, przy pomocy funkcji writelines(). Należy pamiętać konwersji na string i dodaniu znaku końca wiersza. Wykorzystamy do tego funkcję anonimową.

In [11]:
f = open("wynik2.txt",mode='w')
miesiace = map(lambda x: x+'\n',mce)
f.writelines(miesiace)
f.close()

Operacje odczytu i zapisu w języku python są bardzo rozbudowane. Pod tym linkiem można znaleść rozbudowany opis tych i innych operacji wejścia wyjścia. [https://www.tutorialspoint.com/python3/python_files_io.htm]

Czytanie i zapis obiektów języka Python

Tworzone w ramach kodu złożone obiekty na przykład tabele, zagnieżdzonych listy, obiekty klas czy innych struktury istnieją tak długo jak długo wykonywany jest kod, a dokładniej jak długo kod przetwarza blok danych do zasięgu którego należy dany obiekt. Jeżeli obiekt zamierzamy wykorzystać w innych fragmentach kodu, możemy do tego celu użyć obiektu picle, który pozwala na zapis dowolnie złożonej struktury, a następnie wczytanie jej w innym skrypcie. Z pakietu istotne są dwie funkcje dump() pozwalająca zachować plik oraz load() przypisująca zawartość pliku do zmiennej. Należy pamiętać, że proces zapisu wymaga wcześniejszego otwarcia pliku, który zamierzamy zepisać lub otworzyć.

Note: Python 2:

Python 2 posiada dwa moduły pickle - starty i bardzo wolny pickle, oraz nowy, napisany w języyku c cpickle. W przypadku użycia Pythona 2 należy linię import pickle zastępić przez import cpickle as pickle. Python 3 korzysta już z przyspieszonej wersji modułu.

In [12]:
import pickle
lista = [0,1,2,3,4,[50,51,52,53,54],6]
pickle.dump(lista,open( "plik.p", "wb" ))
pikle = pickle.load(open( "plik.p", "rb" ))
In [13]:
print(pikle)
[0, 1, 2, 3, 4, [50, 51, 52, 53, 54], 6]