Zanurkuj w Pythonie/Praca na plikach
Praca z obiektami plików
edytujPython posiada wbudowaną funkcję open
, służącą do otwierania plików z dysku. open
zwraca obiekt pliku posiadający metody i atrybuty, dzięki którym możemy dostać się do pliku i wykonywać na nim pewne operacje.
>>> f = open("/muzyka/_single/kairo.mp3", "rb") #(1) >>> f #(2) <open file '/muzyka/_single/kairo.mp3', mode 'rb' at 010E3988> >>> f.mode #(3) 'rb' >>> f.name #(4) '/muzyka/_single/kairo.mp3'
- Metoda
open
przyjmuje do trzech argumentów: nazwę pliku, tryb i argument buforowania. Tylko pierwszy z nich, nazwa pliku, jest wymagany; pozostałe dwa są opcjonalne. Jeśli nie są podane, plik zostanie otwarty do odczytu w trybie tekstowym. Tutaj otworzyliśmy plik do odczytu w trybie binarnym. (print open.__doc__
da nam świetne objaśnienie wszystkich możliwych trybów.) - Metoda
open
zwraca obiekt (w tym momencie nie powinno to już być zaskoczeniem). Obiekt pliku ma kilka użytecznych atrybutów. - Atrybut
mode
obiektu pliku mówi nam, w jakim trybie został on otwarty. - Atrybut
name
zwraca ścieżkę do pliku, który jest dostępny z tego obiektu.
Czytanie z pliku
edytujOtworzywszy plik, będziemy chcieli odczytać z niego informacje, tak jak pokazano to w następnym przykładzie.
>>> f <open file '/muzyka/_single/kairo.mp3', mode 'rb' at 010E3988> >>> f.tell() #(1) 0 >>> f.seek(-128, 2) #(2) >>> f.tell() #(3) 7542909 >>> tagData = f.read(128) #(4) >>> tagData 'TAGKAIRO****THE BEST GOA ***DJ MARY-JANE*** Rave Mix 2000http://mp3.com/DJMARYJANE \037' >>> f.tell() #(5) 7543037
- Obiekt pliku przechowuje stan otwartego pliku. Metoda
tell
zwraca aktualną pozycję w otwartym pliku. Z uwagi na to, że nie robiliśmy jeszcze nic z tym plikiem, aktualna pozycja to0
, czyli początek pliku. - Metoda
seek
obiektu pliku służy do poruszania się po otwartym pliku. Jej drugi argument określa znaczenie pierwszego argumentu; jeśli argument drugi wynosi0
, oznacza to, że pierwszy argument odnosi się do pozycji bezwzględnej (czyli licząc od początku pliku),1
oznacza przeskok do innej pozycji względem pozycji aktualnej (licząc od pozycji aktualnej),2
oznacza przeskok do danej pozycji względem końca pliku. Jako że tagi MP3, o które nam chodzi, przechowywane są na końcu pliku, korzystamy z opcji2
i przeskakujemy do pozycji oddalonej o 128 bajtów od końca pliku. - Metoda
tell
potwierdza, że rzeczywiście zmieniliśmy pozycję pliku. - Metoda
read
czyta określoną liczbę bajtów z otwartego pliku i zwraca dane w postaci łańcucha znaków, które zostały odczytane. Opcjonalny argument określa maksymalną liczbę bajtów do odczytu. Jeśli nie zostanie podany argument,read
będzie czytał do końca pliku. (W tym przypadku moglibyśmy użyć samegoread()
, ponieważ wiemy dokładnie w jakiej pozycji w pliku jesteśmy i w rzeczywistości odczytujemy ostanie 128 bajtów.) Odczytane dane przypisujemy do zmiennejtagData
, a bieżąca pozycja zostaje uaktualniana na podstawie ilości odczytanych bajtów. - Metoda
tell
potwierdza, że zmieniła się bieżąca pozycja. Jeśli pokusimy się o wykonanie obliczenia, zauważymy, że po odczytaniu128
bajtów aktualna pozycja wzrosła o128
.
Zamykanie pliku
edytujOtwarte pliki zajmują zasoby systemu, a inne aplikacje czasami mogą nie mieć do nich dostępu (zależy to od trybu otwarcia pliku), dlatego bardzo ważne jest zamykanie plików tak szybko, jak tylko skończymy na nich pracę.
>>> f <open file '/muzyka/_single/kairo.mp3', mode 'rb' at 010E3988> >>> f.closed #(1) False >>> f.close() #(2) >>> f <closed file '/muzyka/_single/kairo.mp3', mode 'rb' at 010E3988> >>> f.closed #(3) True >>> f.seek(0) #(4) Traceback (most recent call last): File "<stdin>", line 1, in ? ValueError: I/O operation on closed file >>> f.tell() Traceback (most recent call last): File "<stdin>", line 1, in ? ValueError: I/O operation on closed file >>> f.read() Traceback (most recent call last): File "<stdin>", line 1, in ? ValueError: I/O operation on closed file >>> f.close() #(5)
- Atrybut
closed
obiektu pliku mówi, czy plik jest otwarty, czy też nie. W tym przypadku plik jeszcze jest otwarty (closed
jest równeFalse
). - Aby zamknąć plik należy wywołać metodę
close
obiektu pliku. Zwalnia to blokadę, która nałożona była na plik (jeśli była nałożona), oczyszcza buforowane dane (jeśli jakiekolwiek dane w nim występują), które system nie zdążył jeszcze rzeczywiście zapisać, a następnie zwalnia zasoby. - Atrybut
closed
potwierdza, że plik jest zamknięty. - To że plik został zamknięty, nie oznacza od razu, że obiekt przestaje istnieć. Zmienna
f
będzie istnieć póki nie wyjdzie poza swój zasięg, lub nie zostanie skasowana ręcznie. Aczkolwiek żadna z metod służących do operowania na otwartym pliku, nie będzie działać od momentu jego zamknięcia; wszystkie rzucają wyjątek. - Wywołanie
close
na pliku uprzednio zamkniętym nie zwraca wyjątku; w przypadku błędu cicho sobie z nim radzi.
Błędy wejścia/wyjścia
edytujZrozumienie kodu fileinfo.py z poprzedniego rozdziału, nie powinno już być problemem. Kolejny przykład pokazuje, jak bezpiecznie otwierać i zamykać pliki oraz jak należycie obchodzić się z błędami.
try: #(1)
fsock = open(filename, "rb", 0) #(2)
try:
fsock.seek(-128, 2) #(3)
tagdata = fsock.read(128) #(4)
finally: #(5)
fsock.close()
except IOError: #(6)
pass
- Ponieważ otwieranie pliku i czytanie z niego jest ryzykowne, a także operacje te mogą rzucić wyjątek, cały ten kod jest ujęty w blok
try...except
. (Hej, czy zestandaryzowane wcięcia nie są świetne? To moment, w którym zaczynasz je doceniać.) - Funkcja
open
może rzucić wyjątekIOError
. (Plik może nie istnieć.) - Funkcja
seek
może rzucić wyjątekIOError
. (Plik może być mniejszy niż 128 bajtów.) - Funkcja
read
może rzucić wyjątekIOError
. (Być może dysk posiada uszkodzony sektor, albo plik znajduje się na dysku sieciowym, a sieć właśnie przestała działać.) - Nowość: blok
try...finally
. Nawet po udanym otworzeniu pliku przezopen
chcemy być całkowicie pewni, że zostanie on zamknięty niezależnie od tego, czy metodyseek
iread
rzucą wyjątki, czy też nie. Właśnie do takich rzeczy służy bloktry...finally
: kod z blokufinally
zostanie zawsze wykonany, nawet jeśli jakaś instrukcja blokutry
rzuci wyjątek. Należy o tym myśleć jak o kodzie wykonywanym na zakończenie operacji, niezależnie od tego co działo się wcześniej. - Nareszcie poradzimy sobie z wyjątkiem
IOError
. Może to być wyjątek wywołany przez którąkolwiek z funkcjiopen
,seek
, czyread
. Tym razem nie jest to dla nas istotne, gdyż jedyną rzeczą, którą zrobimy to zignorowanie tego wyjątku i kontynuowanie dalszej pracy programu. (Pamiętajmy,pass
jest wyrażeniem Pythona, które nic nie robi.) Takie coś jest całkowicie dozwolone; to że przechwyciliśmy dany wyjątek, nie oznacza, że musimy z nim cokolwiek robić. Wyjątek zostanie potraktowany tak, jakby został obsłużony, a kod będzie normalnie kontynuowany od następnej linijki kodu po blokutry...except
.
Zapisywanie do pliku
edytujJak można przypuszczać, istnieje również możliwość zapisywania danych do plików w sposób bardzo podobny do odczytywania. Wyróżniamy dwa podstawowe tryby zapisywania plików:
- w trybie "append", w którym dane będą dodawane na końcu pliku
- w trybie "write", w którym plik zostanie nadpisany.
Oba tryby, jeśli plik nie będzie istniał, utworzą go automatycznie, dlatego nie ma potrzeby na fikuśne działania typu "jeśli dany plik jeszcze nie istnieje, utwórz nowy pusty plik, aby móc go otworzyć po raz pierwszy". Po prostu otwieramy plik i zaczynamy do niego zapisywać dane.
>>> logfile = open('test.log', 'w') #(1) >>> logfile.write('udany test') #(2) >>> logfile.close() >>> print open('test.log').read() #(3) udany test >>> logfile = open('test.log', 'a') #(4) >>> logfile.write('linia 2') >>> logfile.close() >>> print open('test.log').read() #(5) udany testlinia 2
- Zaczynamy odważnie: tworzymy nowy lub nadpisujemy istniejący plik test.log, a następnie otwieramy do zapisu. (Drugi argument
"w"
oznacza otwieranie pliku do zapisu.) Tak, jest to dokładnie tak niebezpieczne, jak brzmi. Miejmy nadzieję, że poprzednia zawartość pliku nie była istotna, bo już jej nie ma. - Dane do nowo otwartego pliku dodajemy za pomocą metody
write
obiektu zwróconego przezopen
. - Ten jednowierszowiec otwiera plik, czyta jego zawartość i drukuje na ekran.
- Przypadkiem wiemy, że test.log istnieje (w końcu właśnie skończyliśmy do niego pisać), więc możemy go otworzyć i dodawać dane. (Argument
"a"
oznacza otwieranie pliku w trybie dodawania danych na koniec pliku.) Właściwie moglibyśmy to zrobić nawet wtedy, gdyby plik nie istniał, ponieważ otworzenie pliku w trybie"a"
spowoduje jego powstanie, jeśli będzie to potrzebne. Otworzenie w trybie"a"
nigdy nie uszkodzi aktualnej zawartości pliku. - Jak widać, zarówno pierwotnie zapisane, jak i dopisane dane, aktualnie znajdują się w pliku test.log. Należy zauważyć, że znaki końca linii nie są uwzględnione. Jako że nie zapisywaliśmy znaków końca linii w żadnym z przypadków, plik ich nie zawiera. Znaki końca linii możemy zapisać za pomocą symbolu
"\n"
. Z uwagi na fakt, iż tego nie zrobiliśmy, całość danych w pliku wylądowała w jednej linijce.
Materiały dodatkowe
edytuj- Python Tutorial opisuje, jak czytać i zapisywać pliki, w tym także, jak czytać pliki linia po linii lub jak wczytać wszystkie linie na raz do listy
- Python Library Reference omawia wszystkie metody obiektu pliku.