Ker poskušamo v nekaj ur predavanj strpati celoten tečaj programiranja, pa še marsikaj zraven, se bomo zdaj naučili dveh stvari hkrati: še druge oblike zank in branja datotek.
Zanka while
se je vrtela, dokler je bil resničen določen pogoj. Ta je doslej - in bo pogosto tudi v prihodnje - spraševal po vrednosti kake spremenljivke, ki jo bomo spreminjali znotraj zanke. Tipičen primer je bila zanka, ki je izpisala števila od 1 do 10.
x = 1
while x <= 10:
print(x)
x = x + 1
Druga vrsta zanke je for
. Ta kar sama spreminja spremenljivko: z zanko for
gremo prek kake reči, na primer vrstic datoteke, elementov nekega seznama, znakov v nekem nizu...
Recimo, da imamo datoteko z imenom teze.txt
, ki vsebuje teže nekega vzorca ljudi; v vsaki vrstici je po ena številka. V teh zapiskih se nahaja v poddirektoriju datoteke
, torej je njeno ime (relativno glede na tole beležnico) datoteke/teze.txt
. (Kot ločilo med direktoriji navadno - torej povsod razen na nekaterih mestih v operacijskem sistemu Windows -- uporabljamo navadno poševnico in ne vzvratne.) V datoteki so naslednja števila:
Za začetek le preberimo in izpišimo njene elemente.
teze = open("datoteke/teze.txt")
for vrstica in teze:
print(vrstica)
72 85 70 82 50 64
V prvi vrstici smo poklicali funkcijo open
, ki odpre datoteko in jo "vrne". Kaj pomeni "vrniti datoteko"? Pač, vrne nekaj, kar predstavlja odprto datoteko. Spremenljivka teze
tako ni ne int
ne float
ne str
, temveč gre za spremnljivko tipa ... no, izpišimo jo, pa bomo videli:
teze
<_io.TextIOWrapper name='datoteke/teze.txt' mode='r' encoding='UTF-8'>
Ja, spremenljivka tipa _io.TextIOWrapper
. Ni pomembno. Predstavlja pač odprto datoteko.
Zanka for
po vrsticah bere to datoteko. Obrnila se bo tolikokrat, kolikor je vrstic, in spremenljivki vrstica
vsakič priredila vsebino pravkar prebrane vrstice - kot niz. (Odkod prazne vrstice med njimi? Na koncu vsake vrstice v datoteki je poseben znak, ki pove, da je vrstice konec. Funkcija print
ga upošteva tako, da gre v novo vrstico. Ker pa gre print
na koncu izpisa tudi sam od sebe v novo vrstico, dobimo prazne vrstice.)
Kaj pa, če bi te nize pretvorili v števila?
teze = open("datoteke/teze.txt")
for vrstica in teze:
teza = int(vrstica)
print(teza)
72 85 70 82 50 64
Zdaj pa izračunajmo poprečno težo. Znotraj zanke tež ne bomo izpisovali temveč seštevali, poleg tega pa si bomo zapomnili, koliko tež smo prebrali.
n = 0
vsota = 0
teze = open("datoteke/teze.txt")
for vrstica in teze:
teza = int(vrstica)
vsota += teza
n += 1
print("Poprečna teža je", vsota / n)
Poprečna teža je 70.5
Zanka for
ni edini način za branje datotek. Ne glede na to, kako jih beremo, pa jih lahko beremo le naprej. Skakati nazaj po datoteki je težko ali celo nemogoče. In ko je nekaj prebrano, je prebrano.
teze = open("datoteke/teze.txt")
for vrstica in teze:
print(vrstica)
85 70 82 50 64
for vrstica in teze:
print(vrstica)
Druga zanka ne izpiše ničesar, saj se je datoteka teze
že iztekla.
Ko odpremo datoteko, moramo povedati, ali jo bomo brali ali bomo vanjo pisali. To storimo z dodatnim argumentom, ki je lahko "r"
ali "w"
. Če ga izpustimo - kot smo ga izpustili zgoraj -, bomo datoteko brali.
Pa jo zdaj pišimo.
dat = open("krneki.txt", "w")
dat.write("Prva vrstica.\n")
dat.write("Druga vrstica.\n")
dat.close()
V prvi vrstici torej odpremo datoteko in z dodatnim argumentom povemo, da bomo vanjo pisali. Če datoteka s tem imenom že obstaja, jo povozimo.
Nato vanjo napišemo dve vrstici. To storimo s funkcijo write
, ki pa - za razliko od print
, input
in open
- ni funkcija "kar tako", temveč pripada datoteki, v našem primeru datoteki dat
. O teh rečeh bomo več povedali prihodnjič, za zdaj naj zadošča, da vemo, da v datoteko dat
pišemo tako, da kličemo funkcijo dat.write
.
Funkcija write
sprejme en argument, ki mora biti niz. Če želimo izpisati število, ga moramo s str
pretvoriti v niz.
x = 42
dat.write(str(x))
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-22-5561854fa793> in <module>() 1 x = 42 ----> 2 dat.write(str(x)) ValueError: I/O operation on closed file.
A, ups, v dat
ne moremo (več) pisati, ker smo jo zaprli, tako da smo v zadnji vrstici poklicali dat.close
. Tudi close
je, tako kot write
, funkcija, ki pripada datoteki dat
.
Čemu je potrebno zapiranje? Python (in večina drugih jezikov) v datoteko ne piše sproti, temveč vse, kar je potrebno zapisati, shranjuje v začasen pomnilnik. Na disk piše šele, ko je ta pomnilnik poln (tedaj zapiše shranjeno in začne shranjevati na novo) ali pa takrat, ko zapremo datoteko.
Kaj pa so tisti \n
na koncu nizov? To pa je znak za konec vrstice. Brez njega bi se oba niza izpisala v isto vrstico.
Nekaterih datotek ni možno brati po vrsticah, saj ne vsebujejo besedila. Še več; besedilne datoteke Python (in drugi jeziki) navadno malo "popravijo", da je z njimi lažje delati. Različni operacijski sistemi zapisujejo nekatere znake nekoliko različno, zato je od programskega jezika prijazno, da te stvari poenoti.
Če datoteka vsebuje kake nebesedilne podatke, na primer slike ali kake meritve, ki niso zapisane v obliki berljivega besedila, je potrebno Python posvariti, naj datoteko odpre kot "binarno datoteko". To storimo tako, da k drugemu argumentu funkcije open
dodamo še b
.
slika = open("slike/lactuca-sativa.jpg", "rb")
Takšnih datotek se ne bomo učili brati, saj ima vsaka čisto drugačno, specifično obliko. Pač pa jih bomo verjetno odpirali zato, da jih bomo dali kot argument kakim funkcijam iz knjižnic, ki jih bomo klicali, ko bomo obdelovali resnične podatke, ne seznamov tež. A več o tem, ko pride na vrsto.