Tilbake
7.1

7.1 Filinnlesing linje for linje

Lese en tekstfil linje for linje, fjerne linjeskift, splitte på felt og konvertere til tall — nesten alltid steget som fyller klassemodellen med data.

55 min
8 oppgaver
Filinnlesing linje for linje
Din fremgang i kapitlet
0 / 8 oppgaver

Forkunnskaper — sist du var her

Kapitlet bygger på kap. 3.3 — Strenger og
kap. 3.1 — Lister, og bruker klassene fra
kap. 5.1 når fila skal fylle en modell.

De tre tingene som avgjør alt her, friskes opp med kjørt kode.

1. .split() deler en tekst i en liste av felt:

linje = "skrue;120;4" felt = linje.split(";")
print(felt)
print(len(felt), felt[0], felt[2])

Utskrift:

['skrue', '120', '4']
3 skrue 4

2. .rstrip() fjerner blanktegn i enden — også linjeskiftet:

raa = "Ada 24\n" print(len(raa))
ren = raa.rstrip()
print(len(ren))

Utskrift:

7
6

Legg merke til at lengden går fra 7 til 6: linjeskiftet er ett tegn, og det er det eneste som forsvant. Mellomrommet inni teksten ble ikke rørt, fordi
.rstrip() bare tar fra høyre.

3. Felt fra en fil er alltid tekst. Skal du regne, må de gjennom int(...):

felt = "120" print(felt + felt)
print(int(felt) + int(felt))

Utskrift:

120120
240

Har du de tre, er resten mønsteret.

Løkke 1 — Å åpne en fil og lese én linje (~14 min)

Et treningssenter har medlemslista si i en tekstfil. Den ser slik ut:

Innholdet i medlemmer.txt:

Ada 24
Nora 31
Per 19

Tre linjer, hver med et navn, et mellomrom og en alder. Programmet skal lese den inn og gjøre noe med den — og hele Del 7 handler om nettopp den jobben.

En fil leses oppfra og ned, én linje om gangen. Du kan ikke hoppe rundt i den, og du kan ikke lese den samme linja to ganger uten å åpne fila på nytt.

`open(filnavn)`

Åpner en tekstfil for lesing og gir deg et filobjekt som holder styr på hvor langt du har lest:

fil = open("medlemmer.txt")
linje = fil.readline()
print(linje)
fil.close()

Utskrift:

Ada 24

Legg merke til den tomme linjen i utskriften. Den kommer av at readline tar med linjeskiftet på slutten av linja, og print legger til ett til. Det er den første grunnen til at .rstrip() finnes.

Filnavnet er en helt vanlig tekst, og du kan bygge det med +:
serienavn + ".txt". Finnes ikke fila, stopper programmet, og siste linje i feilmeldingen er FileNotFoundError med filnavnet i.

`fil.readline()`

Leser neste linje og gir den som en tekst, med linjeskiftet på slutten.

Filobjektet husker hvor du er. Første kall gir linje 1, andre kall gir linje 2, og så videre — du trenger ingen teller.

Er det ingen flere linjer igjen, gir readline den tomme teksten "". Det er signalet om at fila er slutt, og det er nettopp den verdien løkka bruker som stoppbetingelse.

En blank linje midt i fila gir "\n", ikke "". De to er ikke det samme, og det er derfor løkka ikke stopper for tidlig på en tom linje.

📜Filinnlesing-løkka

Bokas trettende kodemønster, og det du skal kunne skrive uten å tenke:

fil = open(filnavn)
linje = fil.readline()
while linje != "":
    ...gjor noe med linje...
    linje = fil.readline()
fil.close()

Blokken er en mal med plassholdere og er merket som ikke-kjørbar.

Fem faste deler, og hver av dem er verdt poeng:

1. open før løkka.
2. Første readline før løkka — ellers har while ingenting å teste.
3. while linje != "": — løkk til den tomme teksten.
4. linje = fil.readline() NEDERST i løkka. Glemmer du den, leses den samme
linja for alltid, og programmet henger.
5. fil.close() etter løkka.

Legg merke til at readline står to steder. Det er det som ser rart ut første gang, og det er den vanligste kilden til en løkke som aldri stopper.

✏️Eksempel 1: Les hele fila og skriv den ut

Skriv et program som leser medlemmer.txt linje for linje og skriver ut hver linje, uten den ekstra tomme linjen mellom.

Malen satt rett inn, med .rstrip() på hver linje:

Innholdet i medlemmer.txt:

Ada 24
Nora 31
Per 19

fil = open("medlemmer.txt")
linje = fil.readline()
antall = 0 while linje != "":
    print(linje.rstrip())
    antall = antall + 1 linje = fil.readline()
fil.close()
print("Antall linjer:", antall)

Utskrift:

Ada 24
Nora 31
Per 19
Antall linjer: 3

Telleren er lagt inn med vilje. Den viser at filinnlesing-løkka er en helt
vanlig while-løkke: alt du kan om tellere og akkumulatorer fra
kap. 1.3, virker uendret.

Slik ville sensor sett på det: fem ting gir uttelling hver for seg — open,
første readline, while linje != "":, kroppen, og readline nederst.
Glemmer du fil.close(), er trekket lite; glemmer du den nederste readline, henger programmet, og det koster mer.

Prøv å fjerne .rstrip() i hodet: da ville hver utskriftslinje fått en tom
linje under seg, fordi linjeskiftet fra fila kommer i tillegg til det print legger på.

📝Oppgave 1

(Innstegsoppgave — gjengivelse.) Under står filinnlesing-løkka med fire linjer stokket om.

a) Sett linjene i riktig rekkefølge: fil.close(), while linje != "":,
fil = open("data.txt"), linje = fil.readline().

b) Hvilken av linjene må stå to ganger, og hvor?

c) Hva skjer hvis du glemmer den siste av de to?

Løkke 2 — Rens først, del så (~14 min)

En linje fra en fil er sjelden bare tekst du vil skrive ut. Som regel er den et
datasett med flere felt, og du vil ha tak i hvert av dem.

Rekkefølgen på de to stegene er fast og verdt å tatovere: .rstrip() FØR
.split().
Motsatt rekkefølge gir en stum feil som dukker opp langt unna.

`linje.rstrip().split(sep)`

Malen for å hente feltene ut av en linje. Rens først, del så:

linje = "skrue;120;4\n" felt = linje.rstrip().split(";")
print(felt)
print(felt[0], felt[2])

Utskrift:

['skrue', '120', '4']
skrue 4

De to metodene leses fra venstre: .rstrip() gir en ny tekst uten linjeskiftet, og .split(";") deler den i en liste.

Uten argument deler .split() på blanktegn, som passer når feltene er skilt med mellomrom: "Ada 24".split() gir ['Ada', '24'].

Skilletegnet er en del av dataformatet, ikke noe du velger. Bruk det tegnet
fila faktisk bruker.

✏️Eksempel 2: Fra linje til ferdige verdier

Les varer.txt, der hver linje har formen varenavn;antall;pris, og skriv ut navnet og den samlede verdien for hver vare.

Innholdet i varer.txt:
skrue;120;4 mutter;80;2 bolt;45;7

Løkka er den samme som før; det nye ligger i de tre linjene inne i den:

fil = open("varer.txt")
linje = fil.readline()
total = 0 while linje != "":
    felt = linje.rstrip().split(";")
    navn = felt[0]
    antall = int(felt[1])
    pris = int(felt[2])
    verdi = antall * pris print(navn, verdi)
    total = total + verdi linje = fil.readline()
fil.close()
print("Samlet verdi:", total)

Utskrift:

skrue 480 mutter 160 bolt 315
Samlet verdi: 955

De tre navngitte variablene er et bevisst valg. Du kunne skrevet
int(felt[1]) * int(felt[2]) direkte, men da må leseren — og du selv, om en time — telle indekser for å skjønne hva som er hva. Én linje per felt gjør besvarelsen lettere å rette.

Slik ville sensor sett på det: her ligger poengene på løkkestrukturen (fem
deler), på .rstrip().split(";") i riktig rekkefølge, på int(...) rundt begge tallene, og på akkumulatoren utenfor løkka. Hadde du glemt int på prisen, ville
antall * pris gitt en lang tekst i stedet for et tall — og feilen ville dukket opp i utskriften, ikke som en feilmelding.

Merk at felt[0] ikke trenger int. Navnet skal være tekst. Konverter bare
det du faktisk skal regne med.

📝Oppgave 2
Eksamensnivå, sjanger…

En linje fra en fil ser slik ut:

linje = "mandag,3\n"

a) Skriv linjen som deler den i felt på riktig måte.

b) Hva er felt[1] etter delingen, og hvilken type har den?

c) Skriv linjen som gir deg antallet som et tall.

📝Oppgave 3
Eksamensnivå, sjanger A

Hva skriver programmet ut? Fila turer.txt inneholder:

mandag,3 tirsdag,7 onsdag,0 torsdag,5

fil = open("turer.txt")
linje = fil.readline()
dager = []
total = 0 while linje != "":
    felt = linje.rstrip().split(",")
    if int(felt[1]) > 2:
        dager.append(felt[0])
        total = total + int(felt[1])
    linje = fil.readline()
fil.close()
print(dager)
print(len(dager), total)

Løkke 3 — Fila fyller modellen (~14 min)

Nå kommer selve eksamensoppgaven. Fila inneholder sjelden bare tall som skal summeres — den inneholder objektene programmet skal jobbe med.

Bestillingen i settene er formulert nesten likt hvert år: «les fila og opprett objektene». Mønsteret er filinnlesing-løkka med to nye linjer inne i den: lag objektet, legg det i samlingen.

📜Fil til samling av objekter

Bokas fjortende kodemønster, og den faste fil-deloppgaven i Oppgave 4:

def les_alle(filnavn):
    samling = []
    fil = open(filnavn)
    linje = fil.readline()
    while linje != "":
        felt = linje.rstrip().split(sep)
        obj = Klasse(felt[0], int(felt[1]))
        samling.append(obj)
        linje = fil.readline()
    fil.close()
    return samling

Blokken er en mal med plassholdernavn og er merket som ikke-kjørbar.

Tre ting i tillegg til filinnlesing-løkka:

1. Samlingen opprettes FØR løkka[] eller {}. Inne i løkka ville den
blitt nullstilt hver runde.
2. Objektet lages av feltene, med int(...) på det som skal være tall.
3. return ETTER løkka, ikke inne i den. Returnerer du inne, får du bare
det første elementet — det er feilkode #7, for tidlig retur.

Skal samlingen være en ordbok, byttes samling.append(obj) med
samling[felt[0]] = obj, og alt annet står.

✏️Eksempel 3: Fra fil til liste av objekter

Fila medlemmer.txt har linjer på formen navn alder. Skriv les_medlemmer(filnavn) som returnerer en liste av Medlem-objekter, med alderen som heltall.

Innholdet i medlemmer.txt:
Ada 24
Nora 31
Per 19

Klassen først, så innlesingsfunksjonen, så bruken:

class Medlem:
    def __init__(self, navn, alder):
        self._navn = navn self._alder = alder

    def hent_alder(self):
        return self._alder

    def __str__(self):
        return self._navn + " (" + str(self._alder) + ")"

def les_medlemmer(filnavn):
    medlemmer = []
    fil = open(filnavn)
    linje = fil.readline()
    while linje != "":
        felt = linje.rstrip().split()
        m = Medlem(felt[0], int(felt[1]))
        medlemmer.append(m)
        linje = fil.readline()
    fil.close()
    return medlemmer

alle = les_medlemmer("medlemmer.txt")
for m in alle:
    print(m)
print("Antall:", len(alle))

sum_alder = 0 for m in alle:
    sum_alder = sum_alder + m.hent_alder()
print("Sum alder:", sum_alder)

Utskrift:

Ada (24)
Nora (31)
Per (19)
Antall: 3
Sum alder: 74

Legg merke til .split() uten argument. Feltene i denne fila er skilt med
mellomrom, og den formen deler på blanktegn av seg selv.

Summeringen nederst beviser at konverteringen virket. Hadde int(...) vært
glemt, ville sum_alder = sum_alder + m.hent_alder() stoppet programmet med
TypeError: unsupported operand type(s) for +: 'int' and 'str' — og det er faktisk en av de snille formene feilkode #4 tar, fordi den i det minste sier fra.

Slik ville sensor sett på det: dette er en typisk 8–10-poengs deloppgave.
Poengene ligger på lista før løkka, filinnlesing-løkkas fem deler,
.rstrip().split(), int(...) på alderen, opprettelsen av objektet,
append, og return etter løkka. Hver av dem teller for seg.

— naturlig pausepunkt —

📝Oppgave 4
Eksamensnivå, sjanger…

Fila containere.txt på en resirkuleringsstasjon har linjer på formen materiale;fylling, der fyllingen er et prosenttall.

a) Skriv klassen Container med konstruktør, en aksessor for fyllingen, og
en __str__ på formen glass: 40 %.

b) Skriv les_containere(filnavn) som returnerer en liste av
Container-objekter.

c) Skriv kode som leser fila, skriver ut alle containerne, og teller hvor
mange som har fylling på minst 80.

📝Oppgave 5
Eksamensnivå, sjanger A

Hva skriver programmet ut? Fila
varer.txt inneholder:

skrue;120;4 mutter;80;2 bolt;45;7

fil = open("varer.txt")
linje = fil.readline()
navn = []
while linje != "":
    felt = linje.split(";")
    navn.append(felt[0])
    linje = fil.readline()
fil.close()
print(navn)
print(len(navn))

Se nøye på delingen.

Løkke 4 — Header, filnavn og den korte løkkeformen (~13 min)

Tre praktiske detaljer til slutt. Alle tre har dukket opp i settene, og alle tre er raske å lære.

Å hoppe over en header-linje

Noen filer har en overskriftslinje øverst som ikke er data. Løsningen er ett ekstra readline-kall før løkka:

fil = open("sykler.txt")
header = fil.readline()
print("Header:", header.rstrip())
linje = fil.readline()
antall = 0 while linje != "":
    antall = antall + 1 linje = fil.readline()
fil.close()
print("Datalinjer:", antall)

Utskrift:

Header: kode stativ turer
Datalinjer: 3

Det ene kallet «bruker opp» første linje, og løkka starter på linje 2. Du trenger ikke å ta vare på headeren; fil.readline() alene på en linje er nok.

Sjekk alltid om fila har en header før du koder. Har den det og du glemmer å
hoppe over den, prøver programmet å konvertere ordet turer til et tall, og stopper med ValueError: invalid literal for int() with base 10: 'turer'.

Å bygge filnavnet

Filnavnet er en helt vanlig tekst og kan settes sammen med +:

navn = "Kolonier" filnavn = navn + ".txt" fil = open(filnavn)
linje = fil.readline()
antall = 0 while linje != "":
    antall = antall + 1 linje = fil.readline()
fil.close()
print(filnavn, "har", antall, "spillere")

Utskrift:

Kolonier.txt har 3 spillere

Formen navn + ".txt" er en gjenganger i settene: hvert objekt har sin egen fil, og navnet på objektet gir navnet på fila.

Punktumet og filendelsen må skrives inn selv — navn + "txt" gir Koloniertxt, og programmet stopper med FileNotFoundError.

`for linje in fil:`

En kortere form som gjør det samme som filinnlesing-løkka:

fil = open("medlemmer.txt")
for linje in fil:
    print(linje.rstrip())
fil.close()

Utskrift:

Ada 24
Nora 31
Per 19

Den er kortere og har ingen risiko for en uendelig løkke, siden det ikke finnes noen readline du kan glemme.

Boka bruker likevel while-formen som standard, av to grunner. Fagets egne
løsningsforslag skriver den, og den lar deg gjøre ting midt i lesingen — som å hoppe over en header eller å lese to linjer om gangen — som den korte formen ikke gjør like enkelt.

Kan du begge, er du trygg uansett hvilken form oppgaveteksten viser.

`fil.close()`

Lukker fila og frigjør den. Står etter løkka, aldri inne i den.

Lukker du fila inne i løkka, stopper programmet på neste readline, og siste linje i feilmeldingen er ValueError: I/O operation on closed file..

Glemmer du close helt, virker programmet som regel likevel — men det er en liten trekkfeil, og det er en dårlig vane i større programmer. Skriv den med én gang du har skrevet open, så glemmer du den ikke.

✏️Eksempel 4: Full fil-deloppgave på eksamensnivå

Et treningssenter har en fil okter.txt med linjer på formen
dag;aktivitet;antall. Skriv en klasse Okt og en funksjon som leser fila og returnerer en ordbok fra aktivitetsnavn til samlet antall deltakere. Fila har ingen header.

Innholdet i okter.txt:
mandag;spinning;14 mandag;yoga;9 tirsdag;spinning;20 onsdag;yoga;6

Legg merke til at samme aktivitet står på flere linjer. Da er teller-idiomet fra
kap. 3.2 svaret — nå med data fra en fil:

class Okt:
    def __init__(self, dag, aktivitet, antall):
        self._dag = dag self._aktivitet = aktivitet self._antall = antall

    def hent_aktivitet(self):
        return self._aktivitet

    def hent_antall(self):
        return self._antall

    def __str__(self):
        return self._dag + ": " + self._aktivitet + " (" + str(self._antall) + ")"

def les_okter(filnavn):
    okter = []
    fil = open(filnavn)
    linje = fil.readline()
    while linje != "":
        felt = linje.rstrip().split(";")
        okter.append(Okt(felt[0], felt[1], int(felt[2])))
        linje = fil.readline()
    fil.close()
    return okter

def deltakere_per_aktivitet(okter):
    d = {}
    for o in okter:
        a = o.hent_aktivitet()
        if a not in d:
            d[a] = 0 d[a] = d[a] + o.hent_antall()
    return d

alle = les_okter("okter.txt")
for o in alle:
    print(o)
print(deltakere_per_aktivitet(alle))

Utskrift:

mandag: spinning (14)
mandag: yoga (9)
tirsdag: spinning (20)
onsdag: yoga (6)
{'spinning': 34, 'yoga': 15}

Jobben er delt i to funksjoner med vilje. Den ene leser fila, den andre
regner. Det er slik oppgaveteksten som regel deler den også — «c) les fila» og
«d) skriv en metode som …» — og det gjør at du kan få full uttelling på den ene selv om du står fast på den andre.

Teller-idiomet er uendret fra kap. 3.2:
if a not in d: og d[a] = 0 før du legger til. Uten den sjekken stopper programmet med KeyError på den første aktiviteten — det er feilkode #14.

Legg merke til rekkefølgen i ordboka. spinning kommer først fordi den ble
opprettet først, ikke fordi den er størst. Ordbøker husker innsettingsrekkefølgen.

Slik ville sensor sett på det: dette er en 12–15-poengs deloppgave i den
store oppgaven. Poengene ligger på klassen, filinnlesing-løkka, int(...) på antallet, ordboka opprettet før løkka, if a not in d:-sjekken, akkumuleringen, og return etter løkka. Har du glemt sjekken, står resten — men programmet stopper, og det koster mer enn en stum feil.

📝Oppgave 6
Eksamensnivå, sjanger…

Fila sykler.txt har en overskriftslinje øverst, og deretter linjer på formen kode stativ turer:

kode stativ turer
R31 Blindern 12
R48 Majorstuen 5
R07 Toyen 23

a) Skriv les_sykler(filnavn) som returnerer en ordbok fra sykkelkode
til antall turer, og som hopper over headeren.

b) Bruk funksjonen og skriv ut ordboka og det samlede antallet turer.

c) Hva ville skjedd om du glemte å hoppe over headeren?

📝Oppgave 7
Eksamensnivå, sjanger F

En brettspillklubb har én fil per spill. Fila heter <spilltittel>.txt og inneholder ett navn per linje — de som eier spillet.

Filene Kolonier.txt og Ordjakt.txt inneholder:

Ada
Nora
Per

Ada
Per

a) Skriv klassen Spill med konstruktør som tar tittelen, en tom liste av
eiere, og metoden les_eiere(self) som bygger filnavnet av tittelen og fyller lista.

b) Legg til antall_eiere(self) og en __str__ på formen
Kolonier (3 eiere).

c) Lag de to spillene, les inn eierne, og skriv ut begge.

📝Oppgave 8
Eksamensnivå, sjanger I

En medstudent har skrevet denne innlesingsfunksjonen:

def les_varer(filnavn):
    fil = open(filnavn)
    linje = fil.readline()
    while linje != "":
        varer = []
        felt = linje.split(";")
        varer.append(felt[0])
        return varer fil.close()

Fila varer.txt inneholder:

skrue;120;4 mutter;80;2 bolt;45;7

a) Funksjonen har tre feil. Hvilke?

b) Gi et konkret moteksempel: hva gir kallet
les_varer("varer.txt"), og hva skulle det gitt?

c) Skriv den rettede funksjonen.

API- og konstruksjonsliste

Begrepsbank

Begrepsbanken er flashcard- og repetisjonsstoff — den gjentar det du nettopp har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder kjernestoffet.

Tekstfil

En fil som inneholder tegn, organisert i linjer skilt av linjeskift.

Den kan åpnes i en hvilken som helst editor, og det er nettopp derfor eksamensoppgaver bruker den: du kan se hva som står der, og du kan lage din egen testfil på ti sekunder.

Filer med bilder eller lyd er ikke tekstfiler og leses helt annerledes. De er ikke pensum her.

Filobjekt

Det open(...) gir deg. Det er ikke innholdet i fila, men en forbindelse til den, med en usynlig markør for hvor langt du har lest.

Markøren er grunnen til at readline gir en ny linje hver gang uten at du trenger en teller.

Skal du lese den samme fila to ganger, må du åpne den på nytt. Markøren kan ikke spoles tilbake med de konstruksjonene som er pensum her.

Linjeskiftet

Tegnet som avslutter en linje. I Python skrives det \n inne i en tekst, og det er ett tegn, ikke to.

Hver linje readline gir deg, har det på slutten — bortsett fra den aller siste linja i fila, hvis fila ikke slutter med en tom linje.

Du ser det aldri i en utskrift. Det viser seg bare som at teksten fortsetter på neste linje, eller som en ekstra tom linje når du skriver ut en urenset linje.

Den tomme teksten som filslutt
readline gir "" når det ikke er flere linjer igjen. Det er signalet løkka bruker.

En blank linje midt i fila gir "\n", som ikke er den tomme teksten. Løkka stopper derfor ikke der, og det er riktig — en blank linje er data, ikke slutt.

Skriver du while linje.rstrip() != "": i stedet, stopper løkka på den første blanke linja. Det er sjelden det du vil.

Filinnlesing-løkka

Bokas faste mønster, i fem deler: open, første readline,
while linje != "":, kroppen, readline nederst — og close etter.

At readline står to steder, er det som ser rart ut første gang. Grunnen er at
while tester før hver runde, så den trenger en verdi allerede første gang.

Glemmer du den nederste, henger programmet. Det er den mest synlige feilen i hele kapitlet, og den letteste å rette.

Felt

En av bitene du får når du deler en linje: i "skrue;120;4" er "skrue",
"120" og "4" de tre feltene.

Feltene er alltid tekster, også når de ser ut som tall. Skal du regne med et
felt, må det gjennom int(...) — det er feilkode #4.

Rekkefølgen er fast og bestemt av dataformatet, så du henter dem med indeks.
Skriv gjerne én navngitt variabel per felt; det gjør besvarelsen lettere å rette.

Skilletegnet

Tegnet feltene er skilt med: semikolon, komma, kolon eller mellomrom.

Det er en del av dataformatet, ikke noe du velger. Bruker fila semikolon og du skriver .split(","), får du én stor bit i stedet for flere små — og feilen dukker opp langt unna, som en IndexError.

Er formatet uklart i en eksamensoppgave, skriv en kommentar om hvilket skilletegn du har forutsatt. Det er den ene legitime bruken av kommentarer i en besvarelse.

Header-linje

En overskriftslinje øverst i fila som ikke er data.

Hoppes over med ett ekstra fil.readline() før løkka. Du trenger ikke å ta vare på returverdien.

Glemmer du det, prøver programmet å konvertere et ord fra overskriften til et tall, og stopper med ValueError — med selve ordet i feilmeldingen. Ser du et overskriftsord der, vet du med én gang hva som mangler.

Å bygge filnavnet
navn + ".txt" — filnavnet er en helt vanlig tekst og kan settes sammen.

Formen er en gjenganger i settene: hvert objekt har sin egen fil, og navnet på objektet gir navnet på fila.

Punktumet må skrives inn selv. Og skal navnet komme fra en instansvariabel, skriver du self._tittel + ".txt" inne i metoden.

Fil til liste av objekter

Mønsteret bak den faste fil-deloppgaven: lista opprettes før løkka, objektet lages av feltene inne i den, og lista returneres etter.

Tre plasseringer avgjør om det virker: samlingen over løkka, append
inne i den, return etter den.

Skal samlingen være en ordbok, byttes append med d[felt[0]] = obj, og alt annet står.

Fil til ordbok av objekter

Varianten der du skal kunne slå opp på et navn eller en kode senere:
d[felt[0]] = Klasse(...).

Nøkkelen er som regel det første feltet, og verdien er hele objektet — ikke rådataen. Da beholder du tilgangen til objektets metoder.

En ordbok kan aldri ha to like nøkler. Har fila to linjer med samme navn,
overskrives den første uten en advarsel. Er det et problem, må oppgaven si
hva som skal skje.

Feilkode #4 i filsammenheng

Glemt int(...) eller float(...) på et felt.

Symptomene er to. Enten en TypeError når du prøver å regne med teksten — den snille varianten, som sier fra. Eller en sammenligning som svarer feil:
"9" > "10" er sann, fordi tekster sammenlignes tegn for tegn.

Den siste er stum og farlig. Konverter så snart feltet er hentet ut, ikke der du skal bruke det.

Feilkode #8

Glemt .rstrip() eller feil skilletegn.

Den rammer bare det siste feltet når det gjelder .rstrip(), og det gjør den ekstra lumsk: alt annet i programmet virker, og feilen dukker opp den dagen noen sammenligner det siste feltet med noe.

Motgiften er å skrive linje.rstrip().split(sep) som én fast lenke, hver gang, uten å vurdere om du «trenger» rensingen.

`.strip()` mot `.rstrip()`
.rstrip() fjerner blanktegn bare til høyre; .strip() fjerner i begge ender.

For en linje fra en fil er .rstrip() det riktige: det er linjeskiftet på slutten du vil bort med, og et mellomrom i starten kan være meningsfullt.

For et felt som kan ha fått et mellomrom rundt seg — for eksempel hvis fila er skrevet med "skrue ; 120" — er .strip() på hvert felt nyttig. Da renser du først linja, deler, og renser hvert felt.

Å dele opp i to funksjoner

Én funksjon som leser fila, én som regner på resultatet.

Det er slik oppgaveteksten som regel deler den også: «c) les fila og opprett objektene» og «d) skriv en metode som finner …».

Gevinsten på eksamen er konkret: du kan få full uttelling på den ene selv om du står fast på den andre. Og du kan bruke en metode oppgaven beskriver, uten selv å ha skrevet den.

`FileNotFoundError`

Feilmeldingen når fila ikke finnes. Siste linje inneholder filnavnet, slik du skrev det.

De vanligste årsakene er en skrivefeil i navnet, en glemt .txt, eller at fila ligger i en annen mappe enn programmet.

På eksamen får du filnavnet oppgitt i teksten. Skriv det av tegn for tegn — også store og små bokstaver, som teller.

Kommentar om forutsetninger

Den ene legitime bruken av kommentarer i en besvarelse: å dokumentere en antakelse når oppgaveteksten er underspesifisert.

I filoppgaver er det særlig tre ting: hvilket skilletegn du har forutsatt, om fila har en header, og hva du gjør med en linje som mangler et felt.

Én linje er nok: # forutsetter at filen ikke har overskriftslinje. Det viser at valget er bevisst, ikke tilfeldig.

Å lese fila to ganger

Filobjektet husker hvor du er, så når løkka er ferdig, er fila «brukt opp».

Trenger du å gå gjennom dataene på nytt, er svaret nesten alltid å lagre dem i en liste eller ordbok ved første gjennomlesing, og så løpe gjennom samlingen så mange ganger du vil.

Det er også grunnen til at fil-deloppgaven kommer først i Oppgave 4: alle de senere deloppgavene jobber på samlingen, ikke på fila.

Objektet leser sine egne data

Formen der innlesingen er en metode på klassen, ikke en frittstående funksjon: def les_eiere(self): som fyller self._eiere.

Filnavnet bygges da av objektets egen instansvariabel:
open(self._tittel + ".txt").

Metoden returnerer som regel ingenting — den endrer objektet, og resultatet hentes med en aksessor etterpå. Det er en helt vanlig og riktig form i den store oppgaven.

Hvorfor `while`-formen er standard
for linje in fil: er kortere og kan ikke henge. Boka bruker likevel
while-formen.

To grunner: fagets egne løsningsforslag skriver den, og den lar deg gjøre ting midt i lesingen — hoppe over en header, lese to linjer om gangen, eller stoppe på en bestemt verdi.

Kan du begge, er du trygg uansett hvilken form oppgaveteksten viser. Og du kan alltid velge den korte når oppgaven bare ber om «gå gjennom alle linjene».

Rekkefølgen i en ordbok fra fil

Ordboka husker den rekkefølgen nøklene ble opprettet i, ikke når de sist ble endret, og ikke etter størrelse.

Leser du fila ovenfra og ned, får ordboka nøklene i filas rekkefølge. Det er verdt å vite når du skal spore en utskrift av hele ordboka.

Skal noe sorteres, må det gjøres eksplisitt — og bokas stil er en eksplisitt finn-beste-løkke, ikke en ferdig sorteringsfunksjon.

Fil-deloppgaven i Oppgave 4

Den faste plassen filbehandling har på eksamen: én deloppgave inne i den store modelleringen, verdt 5–10 poeng.

Bestillingen er nesten alltid «les fila og opprett objektene», og formatet oppgis i oppgaveteksten med et eksempel på et par linjer.

Skriv av eksempellinjen på kladdepapiret og marker skilletegnet og hvilket felt som er hva, før du koder. To minutter der sparer deg for begge halvdelene av feilkode #8.

Repetisjon — de åtte setningene som må sitte

Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.

Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av Universitetet i Oslo. Dette er ikke offisielt studiemateriell. Les mer.