Tekstifailid, andmete lugemine ja kirjutamine

From Wikiversity

< 11. nädala teemad

Failid andmete hoidmiseks[edit]

Andmete pikemaajaliseks säilitamiseks on sobiv neid kirjutada faili. Siis saab neid ka programmi uuel käivitamisel kasutada. Keerulisemal juhul on selleks mitmesugused andmbeaasifailid, kuid lihtsamaks juhuks sobivad ka tavalised tekstifailid. Fail ei pruugi alati olla andmefail selle otseses mõttes. Kuid siin kursuses keskendume just andmefailide kirjutamisels ja lugemisele. Sealjuures on andmed hoitud tekstifailides. Tekstifailid koosnevad sümbolitest (character) ja andmevahetus tekstifailiga kulgeb stringide kaudu.

Faili kasutamiseks tuleb ta kõigepealt avada. Alles seejärel on sinna võimalik andmeid kirjutada või neid sealt lugeda.

Faili avamisel tagastab vastav funktsioon (open()) nn failiobjekti ja selle objekti abil saab edaspidi nimetatud failiga "suhelda". Funktsioon (OOP mõistetes konstruktor) open() loob failiobjekti.

faili_objekt = open(faili_nimi, ligipääs="r", puhvrikasutus=-1)

Tagastatav faili_objekt on faili-tüüpi muutuja. Faili_nimi on avatava (või loodava) faili nö pärisnimi, mille all ta operatsioonisüsteemi mõttes eksisteerib. Nimi võib sisaldada nii ketta kui kataloogitähiseid.

Ligipääs seab paika, mida failiga teha saab. Vaikimisi avatakse fail ainult lugemiseks (r). Lisaks võib ta avatud olla kirjutamiseks (w) ja uute andmete lõppu lisamiseks (a). Täiendavate võimaluste kohta võib lugeda raamatutest ja abist. Puhvrikasutus on seotud sellega, kui suurte osade kaupa faili infot kirjutatakse või failist loetakse. Negatiivne arv (ja parameetri puudumine) ütleb, et kasutatagu operatsioonisüsteemi vaikeseadeid. Positiivne arv annab korraga loetavate/kirjutatavat puhvri suuruse. Tavaliselt soovitatakse kasutada operatsioonisüsteemi vaikeseadet. Puhvri mõtteks on koguda kokku suurem hulk infot ja see siis korraga faili kirjutada, et kiirendada programmi tööd.

Et failmuutuja on objekt, siis saab temaga tööd teha kasutades meetodeid kasutades (stringide juures sai meetodi ja funktsiooni erinevusest räägitud!!) Esimene oluline meetod on sulgemismeetod

close()

Peale kirjutamist tuleb fail sulgeda. Muidu ei kirjutata sinna viimast puhvritäit andmeid ära ja fail võib jääda poolikuks. Näide 1: fail "asi.txt" avatakse kirjutamiseks ning seejärel suletakse:

fm = open("asi.txt","w")
fm.close()                

NB! Tuleta meelde, kuidas meetodit välja kutsuti - sellest oli juttu stringide materjalis.

Selle tulemusena tekib tühi fail, sest ühtegi faili kirjutamise käsku ei antud.

Faili kirjutamine[edit]

Faili kirjutamiseks on meetod write(). Meetod vajab sisendiks stringi, mille ta faili kirjutab. Seega tuleb näiteks arvulised väärtused enne kirjutamist teisendada stringideks. Lisaks tuleb arvestada, et automaatselt ühtegi reavahetust ei lisata, seega tuleb nende eest ise hoolt kanda. Meetodiga writelines() saab faili kirjutada terve stringidest koosneva listi.

Näide 2: fail "tervitus.txt" avatakse kirjutamiseks, luues samaaegselt failiobjekti fm. Faili kirjutatakse string "Tere talv!" ning seejärel fail suletakse.

fm = open("tervitus.txt","w")
fm.write("Tere talv!")
fm.close()

Failiobjekt on samaaegselt ka nö järjehoidja ja iga järgmine write()-meetodi väljakutse lisab stringi seni kirjutatud teksti lõppu (täiendavate reavahetusteta!). Enne kogu kirjutamise lõppu ei tohi faili sulgeda. Suletud faili ei saa kirjutada ning lisaks läheb järjehoidja kaotsi. Katse kirjutada suletud faili lõppeb täitmisaegse veaga.

Kuna faili kirjutamiseks peab kogu info olema stringide kujul, tuleb igasugune teist tüüpi väljund (peamiselt arvud) selleks teisendada. Stringide saamiseks on kaks peamist moodust. Kõigepealt funktsioon str(), mis parameetriks oleva arvu stringiks teisendab.

Näide 3: faili kirjutatakse arv, mis eelnevalt stringiks teisendatakse

karude_arv = 12
fm.write(str(karude_arv))

Teiseks variandiks on kasutada samasugust vormindamist, mida print()-käsuga kasutada saab. Et vorminduskäsud asuvad stringi sees, on kogu tulemus string ja seega sobilik faili kirjutamiseks.

Näide 4: stringi moodustamine vorminduskäskude abil:

karude_arv = 12
fm.write("Metsas oli %d karu" % (karude_arv))

Muuhulgas annab see võimaluse ujukoma arve püsikoma kujul salvestada.

Failist lugemine[edit]

Failist lugemiseks saab kasutada meetodit readline(). Meetod loeb falist ühe rea (st kõik baidid kuni reavahetuseni, viimane kaasaarvatud) ning see info on võimalik omistada stringitüüpi muutujale. Parameetrina on võimalik ka ette anda korraga loetavate baitide arvu. Seda tehakse siiski vaid erilise vajaduse korral. Meetodiga readlines() saab korraga sisselugeda kogu faili. Tulemuseks on stringidest koosnev list – iga ridafailist on eraldi listi element.

Näide 5 fail "tervitus.txt" avatakse lugemiseks, sellest loetud rida trükitakse ekraanile

fm = open("tervitus.txt", "r")
laused = fm.readlines()
print(laused)
fm.close()

Suuremahulise faili korral ei pruugi terve faili listi lugemine otstarbekas olla. Juba tuttava for-tsükli abil on võimalik lugeda kogu fail välja rida haaval.

Näide 6 faili väljalugemisest rida-haaval - fail avatakse lugemiseks, for-tsükliga loetakse järjest kõik read ja trükitakse need ekraanile.

fm = open("tervitus.txt", "r") 
for rida in fm:
    print(rida)
fm.close()

Loomulikult saab väljatrüki asemel teha kõikvõimalikke muid toiminguid. Failist lugemisel on failiobjekt ka nö järjehoidjaks. Iga järgmine readline()-meetodi käivitamine loeb sisse järgmise rea failist. Mida ja kuidas pihta hakata failist kätte saadud stringidega on juba konkreetse ülesande ja stringitöötluse küsimus. Teades ette faili struktuuri ei pruugi olla võimalik või mõistlik kõiki ridu ühel viisil lugeda ja töödelda. Võimalik, et esimesed read sisaldavad hoopis infot järgnevate ridade ülesehituse kohta. Sel juhul oleks vaja ridade edasisele töötlemisele erineval viisil läheneda. Sest enamasti ei ole sisseloetud stringiga niisama tervikuna midagi pihta hakata.

Et lugemisel reavahetuse märke automaatselt ei likvideerita, siis kasutatakse tihti stringi meetodit strip() rea reavahetuse sümbolist vabanemiseks. Meetod strip() kõrvaldab stringi lõpust mittetrükitavad sümbolid – reavahetused, tühikud, ... Näiteks nii:

rida = fm.readline()
rida = rida.strip()

Nimed ja otsiteed[edit]

Kust faili otsitakse? Ja kuhu fail kirjutatakse? Milline saab olla faili nimi? Mis juhtub siis, kui faili ei leita?

Failidega töötamisel on ka nendele küsimustele vaja vastuseid. Kõigepealt eristatakse absoluutset ja suhtelist failinime ning otsiteed (absolute path, relative path). Ja oluline on ka mõiste jooksev/aktiivne kataloog (current directory) või siis töökataloog (working directory).

Faili nimi kujul "minufail.txt" on suhteline faili nimi ja faili tegelik asukoht sõltub töökataloogist, st faili otsitakse või kirjutatakse töökataloogi. Töökataloogiks on tavaliselt see kataloog, kus paikneb käivitatav programm.

Faili suhteline nimi ja otsitee on näiteks järgmine: "andmed\minufail.txt". See tähendab, et töökataloogis on kataloog andmed ning fail paikneb seal.

Faili absoluutne nimi ja otsitee avaldub aga kujul: "Y:\if10\Maali\Prog_alused\minufail.txt". Faili asukoht esitatakse kataloogipuu juurest lähtudes (mis ei pruugi serveri tegelikust kataloogipuust rääkides absoluutne tõde olla - kui mõni kataloog on kettaks mapitud).

Kõiki eelpool kirjeldatud võimalusi tohib faili nime esitamisel open()-funktsioonis kasutada. Ainus tingimus on, et see korrektne oleks ja et failid-kataloogid olemas oleksid. Võimalusi otsiteede uurimiseks ning failide-kataloogide olemasolu tuvastamiseks pakub moodul os. Kasutusele saab mooduli võtta juba tuntud viisil: import os.

Mõned kasulikud funktsioonid:

os.getcwd() - tagastab töökataloogi nime

Võid seda proovida nii Python Shellis kui programmi abil välja trükkida.

os.path.exists(failinimi) – kas fail on olemas

On boolean-tüüpi funktsioon ning ta annab teada, kas parameetriks olev fail või kataloog eksisteerib. Suhtelise failinime korral otsitakse faili töökataloogi suhtes.

os.listdir(kataloog) – tagastab otsitee listi

List koosneb kataloogi sisuks olevatest failide ja kataloogide nimedest; kataloog võib olla esitatud nii suhtelise kui ka absoluutse nimega.

Vea- ehk erinditöötlus[edit]

Veaolukorrad võivad tekkida faili avamisel lugemiseks, kui soovitud faili ei ole (või teda ei ole selles kohas, kust teda otsitakse). Kui fail avatakse kirjutamiseks, võib viga tekkida juhul kui on määratud olematu kataloogi nimi. Fail ise tekib ju uuena, seega ei pea teda eelnevalt olemas olema. Kuid faili kirjutamiseks soovitud kohta ei pruugi programmi kasutajal olla piisavalt õigusi. Ka see olukord põhjustab faili avamisel vea. Et vea võimalusi on veelgi, on tülikas kirjutada programmi, mis enne avamist mooduli os võimalusi kasutades kõiki vigu kontrollida ja ennetada püüab.

Lihtsam viis on kasutada try: ... except: püünist erindi püüdmiseks:

try:
    fm = open("olematu_fail.txt")
    for rida in fm:
        print(rida)
    fm.close()
except:
    print("Miski on mäda!")

Antud näitest ei ole tegelikkuses suuremat kasu, kuid vea korral saab ka anda võimalusi näiteks uue failinime sisestamiseks jms, et veaolukorrast väljuda. Või vähemalt programmi töö viisakalt lõpetada. Lisaks ei ole try..except püünise lisamine programmi kirjutamise ja silumise ajal hea ka seetõttu, et ainsad vead siin ei pruugi tuleneda faili puudumisest. Ka sisendi töötlemisel saavad tekkida probleemid (nt stringi teisendamisel arvuks) ning üldine veateade, et miski mäda on, ei aita viga mitte kuidagi lokaliseerida.

Seega siis: programmi kirjutades väldi esialgu erinditöötlust, loe täitmisaegseid veateateid ja kõrvalda nende abil vead programmist.

< 11. nädala teemad