4.1 Filinnlesing: les fil → 2D-tabell eller streng
Lese en tekstfil linje for linje og bygge en 2D-tabell (liste av lister) eller én streng — det kanoniske første leddet i hver programmeringsoppgave.
- Filinnlesing står i 100 % av settene (10 av 10 sittinger i arkivet 2014–2019), og er nesten alltid deloppgave a i den store programmeringsoppgaven. Klarer du ikke å lese fila, stopper resten av kjeden.
- Sjangeren heter E — filinnlesing, altså «les fila og bygg datastrukturen». Den er verdt 5–10 % av settet alene, og den er den ene deloppgaven du kan øve deg til full uttelling på.
- Mønsteret er helt fast: åpne fila, gå gjennom den linje for linje, rens linja, del den i felt, konverter tallene, legg raden i tabellen, lukk fila. Sju steg som er de samme uansett om oppgaven handler om bussruter, bibliotekutlån eller strømpriser.
- Du møter også sjanger B — kodesporing, altså «hva skriver dette programmet ut?», med fillesing som innpakning.
Prioritet: må sitte (høyeste av bokas tre nivåer, som er «må sitte», «må kunne» og «bør kjenne til»).
Tre feil tar nesten alle poengene som mistes her, og alle tre er stumme — programmet krasjer ikke, det svarer bare feil:
- #1 — glemt int() eller float(), så tallene fortsatt er tekst.
- #2 — glemt .strip(), så linjeskiftet henger igjen i siste felt.
- #3 — feil skilletegn i .split(), så linja ikke deles slik du tror.
Kapitlet tar ~55 minutter og er delt i seks løkker med tidsanslag. Det er helt greit å ta det over to økter — det naturlige skillet er etter løkke 3.
- kap. 3.2 — 2D-lister: en liste der hvert element selv er en liste, altså en tabell med rader og kolonner. Det er strukturen vi bygger her.
- kap. 3.3 — strenger: .strip(), .split() og indeksering. Det er verktøyene som gjør én tekstlinje om til én rad.
- kap. 3.5 — ordbøker: brukes i løkke 5, der vi leser fila inn i en ordbok i stedet for en tabell.
- kap. 1.1 — konvertering med int() og float().
Vil du se det samme stoffet i en annen innpakning, går det an å lese Filinnlesing linje for linje i IN1000-boka ved siden av. Den bruker en litt annen løkkeform, men jobben er den samme.
Løkke 1 — Å åpne en fil og lese den linje for linje (~10 min)
En værstasjon lagrer døgnmålingene sine i en helt vanlig tekstfil. Den ser slik ut:
Innholdet i maalinger.txt:
Blindern;12.4;61
Kjevik;15.1;54
Værnes;9.8;73Tre linjer. Hver linje har et stedsnavn, en temperatur og en luftfuktighet, skilt med semikolon. Det er alt en «datafil» er i dette faget: tekst, én rad per linje, med et skilletegn mellom feltene.
En tekstfil leses ovenfra og ned, én linje om gangen. Du kan ikke hoppe fram og tilbake i den, og du kan ikke lese den samme linja to ganger uten å åpne fila på nytt.
En tekstfil er en sammenhengende tekst der linjene skilles av et usynlig linjeskifttegn, som i Python skrives '\n'. Tegnet er én bokstav på lik linje med a og ; — det ser du bare ikke.
Derfor gjelder to ting som forvirrer alle første gang:
- Hver linje du leser fra en fil ender på '\n' (den aller siste kan mangle det).
- print legger på sitt eget linjeskift i tillegg. Skriver du ut en ulest-renset linje, får du en blank linje under.
Løsningen på begge er .strip(), som du møter i løkke 2.
Åpner en tekstfil for lesing og gir deg et filobjekt — en peker som husker hvor langt du har lest.
'r' står for read. Det er standardverdien, så open('maalinger.txt') betyr det samme, men boka skriver modusen ut fordi du snart skal skrive 'w', 'a', 'wb' og 'rb' i samme posisjon.
f = open('maalinger.txt', 'r')
forste = f.readline()
print(forste)
f.close()Utskrift:
Blindern;12.4;61
Legg merke til den blanke linja i utskriften: linja fra fila har med seg sitt eget '\n', og print legger på ett til.
Filnavnet er en helt vanlig streng, så du kan bygge det: navn + '.txt'. Finnes ikke fila, stopper programmet med en feil som heter FileNotFoundError — den håndterer vi i kap. 4.3.
Lukker fila og frigjør den. Når du leser, er trekket for en glemt close() lite; når du skriver, kan det koste selve dataene (kap. 4.2).
Regelen i denne boka: hver open har en close, og close står etter løkka — ikke inne i den. Lukker du fila inne i løkka, krasjer neste runde.
Går gjennom fila én linje av gangen, uten at du trenger en teller eller en stoppbetingelse. Løkka avslutter av seg selv når fila er slutt.
Variabelen linje er en streng med hele linja, inkludert linjeskiftet på slutten.
f = open('maalinger.txt', 'r')
for linje in f:
print(linje.strip())
f.close()Utskrift:
Blindern;12.4;61
Kjevik;15.1;54
Værnes;9.8;73Dette er formen boka bruker overalt. Den er kortere og tryggere enn while-varianten med readline(), fordi du ikke kan glemme å lese neste linje.
Navnekollisjon å være klar over: f er filobjektet her. f brukes også som prefikset i en f-streng (f'{x:.2f}', se kap. 3.4). De to har ingenting med hverandre å gjøre, og du ser forskjellen på at f-strengen har en fnutt rett etter f.
Skriv et program som leser maalinger.txt, skriver ut hver linje med linjenummer foran, og til slutt sier hvor mange linjer fila hadde.
maalinger.txt:Blindern;12.4;61
Kjevik;15.1;54
Værnes;9.8;73Selve løkka er hele oppskriften; telleren er en vanlig teller fra kap. 1.3.
f = open('maalinger.txt', 'r')
antall = 0
for linje in f:
antall = antall + 1
print(antall, linje.strip())
f.close()
print('Antall linjer:', antall)Utskrift:
1 Blindern;12.4;61
2 Kjevik;15.1;54
3 Værnes;9.8;73
Antall linjer: 3Slik ville sensor sett på det: fire ting gir uttelling hver for seg — open før løkka, for linje in f:, det du gjør med linja, og close etter løkka. Glemmer du close(), er trekket lite. Setter du close() inne i løkka, stopper programmet på andre runde, og det koster mer.
Dette er også én av flere korrekte løsninger: du kunne telt med len(f.readlines()) i stedet. Begge gir full pott — sensor er ute etter riktig struktur, ikke én bestemt formulering.
(Innstegsoppgave — sjanger B, kodesporing: «hva skriver dette programmet ut?».) Hva skriver dette programmet ut? Skriv utskriften nøyaktig, med linjeskift slik terminalen viser den.
linje = 'Kjevik;15.1;54\n'
print(linje)
print(linje.strip())
print('ferdig')Løkke 2 — Rens først, del så (~12 min)
En linje fra fila er sjelden interessant som én tekst. Du vil ha tak i feltene: stedet, temperaturen, fuktigheten.
Å parse en linje betyr nettopp det: å tolke en tekstlinje og hente ut feltene den består av. Det gjøres i to steg, og rekkefølgen er fast: rens først med .strip(), del så med .split().
Linja 'Kjevik;15.1;54' er for datamaskinen bare 14 tegn. At det er et sted, en temperatur og en luftfuktighet, er noe du vet — og parsingen er der du forteller programmet det.
I dette faget er parsingen alltid den samme lenken: linje.strip().split(separator), etterfulgt av int() eller float() på tallfeltene.
De tre du møter på eksamen er semikolon (';'), komma (',') og tabulator ('\t', ett tabulatortegn). Mellomrom er den fjerde, og den har en egen form: .split() uten argument.
Skriv aldri separatoren på slump. Se på fila først — det tar to sekunder og redder hele oppgaven.
Gir en ny streng uten blanktegn i begge ender — mellomrom, tabulator og linjeskift.
raa = 'Kjevik;15.1;54\n'
print(len(raa))
ren = raa.strip()
print(len(ren))
print(ren)Utskrift:
15
14
Kjevik;15.1;54Lengden går fra 15 til 14: linjeskiftet er ett tegn, og det var det eneste som forsvant. Semikolonene inni linja blir aldri rørt, fordi .strip() bare tar fra endene.
Originalen raa er uendret — strenger kan ikke endres, du får alltid en ny.
Med et argument fjerner .strip() de tegnene du oppgir, fra begge ender, i stedet for blanktegn:
print('***Blindern***'.strip('*'))
print(' Kjevik '.strip())
print('..12.4..'.strip('.'))Utskrift:
Blindern
Kjevik
12.4Merk at argumentet er en mengde tegn, ikke en tekst som må stå samlet: .strip('.*') fjerner både punktum og stjerner. Varianten uten argument er den du bruker i 99 % av filoppgavene.
Deler en streng i en liste av felt, med separatoren som skille. Separatoren selv blir borte.
felt = 'Kjevik;15.1;54'.split(';')
print(felt)
print(len(felt))
print(felt[0], felt[2])Utskrift:
['Kjevik', '15.1', '54']
3
Kjevik 54Antall felt er alltid antall separatorer pluss én. Har en linje tre felt og du ber om felt[3], stopper programmet med IndexError.
Er separatoren fraværende i strengen, får du en liste med ett element — hele strengen. Det er det du ser når du har brukt feil skilletegn.
Uten argument deler .split() på vilkårlig mange blanktegn — ett mellomrom eller ti, og tabulator også. Tomme felt oppstår ikke.
print('Blindern 12.4 61'.split())
print('Blindern 12.4 61'.split(' '))
print('Blindern 12.4 61'.split(' '))Utskrift:
['Blindern', '12.4', '61']
['Blindern', '12.4', '61']
['Blindern', '', '', '12.4', '', '', '', '', '', '61']Se på den siste: med ' ' som eksplisitt separator blir hvert ekstra mellomrom til et tomt felt. Derfor er .split() uten argument den riktige når fila er kolonnejustert med varierende mellomrom.
Bokas parselenke, lest fra venstre: .strip() gir en ny streng uten linjeskiftet, og .split(sep) deler den i felt.
linje = 'Værnes;9.8;73\n'
felt = linje.strip().split(';')
print(felt)
print(felt[-1] == '73')Utskrift:
['Værnes', '9.8', '73']
TrueRekkefølgen er fast. Gjør du det motsatt — linje.split(';') og så .strip() på hele lista — får du ikke engang lov: en liste har ingen .strip().
Bokas viktigste mønster. Det skal sitte så godt at du kan skrive det på papir uten å tenke, for det er deloppgave a i hver eneste programmeringsoppgave.
def les_tabell(filnavn):
tabell = []
f = open(filnavn, 'r')
for linje in f:
tabell.append(linje.strip().split(separator))
f.close()
return tabellBlokken er en mal med en plassholder (separator) og er derfor merket som ikke-kjørbar.
Seks faste deler, og hver av dem gir uttelling for seg:
1. Tom liste før løkka — den blir tabellen.
2. open før løkka.
3. for linje in f:.
4. .strip().split(...) i den rekkefølgen, med riktig separator.
5. append av raden — én rad per linje.
6. close etter løkka, og return tabell til slutt.
Glemmer du return, får kalleren None, og resten av programmet ditt krasjer på noe som ser ut som en helt annen feil.
Skriv les_tabell(filnavn) som leser maalinger.txt og returnerer en 2D-liste med én rad per linje og ett felt per kolonne. Skriv ut tabellen, antall rader, antall kolonner og feltet i rad 1, kolonne 0.
maalinger.txt:Blindern;12.4;61
Kjevik;15.1;54
Værnes;9.8;73def les_tabell(filnavn):
tabell = []
f = open(filnavn, 'r')
for linje in f:
tabell.append(linje.strip().split(';'))
f.close()
return tabell
tabell = les_tabell('maalinger.txt')
print(tabell)
print(len(tabell), len(tabell[0]))
print(tabell[1][0])Utskrift:
[['Blindern', '12.4', '61'], ['Kjevik', '15.1', '54'], ['Værnes', '9.8', '73']]
3 3
KjevikLes utskriften nøye. Ytterlista har tre elementer — én per linje. Hvert element er selv en liste med tre strenger. len(tabell) er antall rader, len(tabell[0]) er antall kolonner i første rad.
Alle feltene er strenger, også '15.1' og '54'. Det er neste løkkes tema, og det er feilkode #1 i praksis.
Slik ville sensor sett på det: dette er mønsterets seks deler, satt rett inn. Har du strukturen riktig, men skriver .split(',') fordi du leste fila for fort, får du fortsatt god uttelling på deloppgaven — riktig overordnet struktur belønnes selv med en slik feil. Men de påfølgende deloppgavene blir vanskeligere, for du drar feilen med deg.
Begge er stumme: programmet krasjer ikke, det svarer bare feil.
#2 — glemt .strip(), så linjeskiftet henger med i siste felt:
linje = 'Kjevik;15.1;54\n'
uten = linje.split(';')
med = linje.strip().split(';')
print(uten)
print(med)
print(uten[2] == '54', med[2] == '54')Utskrift:
['Kjevik', '15.1', '54\n']
['Kjevik', '15.1', '54']
False TrueSe på siste felt. Uten rensing er det '54\n', og sammenligningen med '54' svarer usant — selv om det «ser riktig ut» når du skriver det ut. Feilen rammer bare siste felt, og det gjør den ekstra lumsk.
#3 — feil separator:
linje = 'Kjevik;15.1;54'
print(linje.split(','))
print(len(linje.split(',')))Utskrift:
['Kjevik;15.1;54']
1Delingen på komma finner ingen komma, og du får ett felt med hele linja. Neste linje i programmet ditt ber om felt[1] og stopper med IndexError: list index out of range.
Motgiften mot begge: skriv linje.strip().split(sep) som én lenke, og skriv ut felt for den første linja mens du utvikler. Da ser du med én gang om delingen ble som ventet.
Fila glass.txt har én linje per by med formen by;antall:
Innholdet i glass.txt:
Bergen;120
Tromsø;45
Stavanger;88a) Skriv les_linjer(filnavn) som returnerer en liste med rensede linjer, altså ['Bergen;120', 'Tromsø;45', 'Stavanger;88'].
b) Hva må du endre i funksjonen for å få en 2D-tabell i stedet?
Fila glass.txt inneholder:
Bergen;120
Tromsø;45
Stavanger;88Hva skriver programmet ut?
f = open('glass.txt', 'r')
byer = []
for linje in f:
felt = linje.split(';')
byer.append(felt[0])
f.close()
print(byer)
print(len(byer))Løkke 3 — Konverter tallene (~10 min)
Alt som kommer fra en fil er tekst. Også '54'. Også '15.1'. Filer inneholder ikke tall — de inneholder tegn som ser ut som tall.
Skal du regne, sammenligne eller sortere, må feltet gjennom int() eller float() først. Dette er feilkode #1, og det er den hyppigste feilen i hele faget.
— naturlig pausepunkt: har du lest hit, har du hele deloppgave a. Resten av kapitlet er varianter. —
int(s) gjør en streng om til et heltall, float(s) til et desimaltall. Begge stopper programmet med ValueError hvis strengen ikke ser ut som et tall.felt = ['Kjevik', '15.1', '54']
print(felt[2] + felt[2])
print(int(felt[2]) + int(felt[2]))
print(float(felt[1]) * 2)Utskrift:
5454
108
30.2Første linje er feilen i ren form: + på to strenger skjøter dem sammen. Programmet krasjer ikke — det svarer 5454.
Tommelfingerregel: konverter i samme linje som du henter feltet, mens du husker hva kolonnen betyr. Bruk float for alt som kan ha desimaler (pris, temperatur, tid) og int for antall.
Utvid les_tabell slik at temperaturen blir et flyttall og fuktigheten et heltall, mens stedsnavnet forblir tekst. Regn deretter ut gjennomsnittstemperaturen.
maalinger.txt:Blindern;12.4;61
Kjevik;15.1;54
Værnes;9.8;73def les_maalinger(filnavn):
tabell = []
f = open(filnavn, 'r')
for linje in f:
felt = linje.strip().split(';')
tabell.append([felt[0], float(felt[1]), int(felt[2])])
f.close()
return tabell
data = les_maalinger('maalinger.txt')
print(data)
sum_temp = 0
for rad in data:
sum_temp = sum_temp + rad[1]
print('Snitt:', sum_temp / len(data))Utskrift:
[['Blindern', 12.4, 61], ['Kjevik', 15.1, 54], ['Værnes', 9.8, 73]]
Snitt: 12.433333333333332Nå står tallene uten anførselstegn i utskriften. Det er den enkleste måten å se at konverteringen faktisk skjedde.
Snittet har mange desimaler. Det er slik flyttall er — kan ikke skrives eksakt binært, og Python viser deg hele svaret. Vil du ha to desimaler i en rapport, formaterer du ved utskrift, ikke ved lagring: '{:.2f}'.format(sum_temp / len(data)) eller f'{sum_temp / len(data):.2f}' (se kap. 3.4). Begge former er gyldige; f-strengen er den nyere.
Slik ville sensor sett på det: her ligger poengene på at hvert felt får riktig type, og på at stedsnavnet ikke konverteres. En løsning som konverterer i en egen løkke etterpå, gir like full pott — det finnes flere riktige veier.
Fila stromspris.txt har én linje per prisområde, og feltene er skilt med ett tabulatortegn:
Innholdet i stromspris.txt:
NO1 0.87
NO3 0.42
NO5 0.55a) Skriv les_priser(filnavn) som returnerer en 2D-liste der prisen er et flyttall.
b) Skriv koden som regner ut den høyeste prisen i tabellen, uten å bruke max.
c) Skriv den samme utregningen med max.
Løkke 4 — Overskriftsrad, og hele fila på én gang (~10 min)
Mange datafiler har en overskriftsrad øverst som navngir kolonnene. Den skal ikke inn i tabellen — den er metadata, ikke data. Prøver du å konvertere den, krasjer programmet på ordet dager.
Til gjengjeld er overskriftsraden en gave: den lar deg finne kolonnen ved navn i stedet for å anta hvilken posisjon den har.
Den første linja i en datafil, som navngir kolonnene i stedet for å inneholde data.
Du kjenner den igjen på at feltene er ord der resten av fila har tall. Behandlingen er alltid den samme: les den for seg, før løkka, og bruk den enten til å hoppe over eller til å slå opp kolonnenavn.
Eksamensoppgaven sier alltid om fila har en slik rad. Står det ingenting, har den det ikke.
Leser én linje og flytter posisjonen i fila ett hakk fram. Linja kommer med linjeskiftet på slutten.
Kalt før en for-løkke er dette den vanlige måten å hoppe over overskriftsraden: løkka fortsetter der readline slapp.
f = open('utlaan.txt', 'r')
overskrift = f.readline()
print(overskrift.strip())
antall = 0
for linje in f:
antall = antall + 1
f.close()
print('Datalinjer:', antall)Utskrift:
tittel;låner;dager
Datalinjer: 3Er fila slutt, gir readline() den tomme strengen ''. En blank linje midt i fila gir '\n' — de to er ikke det samme.
Leser hele fila og gir deg en liste med alle linjene, hver med sitt linjeskift.
f = open('maalinger.txt', 'r')
linjer = f.readlines()
f.close()
print(linjer)
print(len(linjer))
print(linjer[0].strip())Utskrift:
['Blindern;12.4;61\n', 'Kjevik;15.1;54\n', 'Værnes;9.8;73\n']
3
Blindern;12.4;61Praktisk når du trenger tilfeldig tilgang til linjene (for eksempel den siste), eller vil vite antallet før du begynner. Ulempen er at hele fila havner i minnet på én gang — irrelevant for en eksamensfil på ti linjer.
Leser hele fila som én streng, med alle linjeskiftene inni.
f = open('maalinger.txt', 'r')
tekst = f.read()
f.close()
print(len(tekst))
print(tekst.count('\n'))
print(tekst.strip().split('\n'))Utskrift:
46
3
['Blindern;12.4;61', 'Kjevik;15.1;54', 'Værnes;9.8;73']Dette er «les fil til streng»-varianten. Den brukes når oppgaven handler om teksten som helhet — telle tegn eller ord, snu teksten, lete etter et delord.
tekst.strip().split('\n') gir deg linjene tilbake som en liste, uten linjeskift. Det er ofte den ryddigste veien.
Finner posisjonen til et kolonnenavn i overskriftsraden, slik at du slipper å anta at «dager» er kolonne 2.
overskrift = ['tittel', 'låner', 'dager']
print(overskrift.index('dager'))
print(overskrift.index('tittel'))Utskrift:
2
0Dette er et eksplisitt fullscore-krav på eksamen (feilkode #4 — anta fast kolonnerekkefølge). Skriver du felt[2], virker koden på akkurat den fila og ryker hvis kolonnene bytter plass. Skriver du felt[overskrift.index('dager')], virker den uansett.
Finnes ikke navnet, stopper programmet med ValueError — det er en ærlig feil, og bedre enn et stille galt svar.
Fila utlaan.txt har en overskriftsrad. Skriv les_utlaan(filnavn) som returnerer overskriften og en 2D-tabell der dagerkolonnen er heltall — uten å anta hvilken posisjon den kolonnen har.
utlaan.txt:tittel;låner;dager
Sult;Ida;14
Markens grode;Omar;21
Naiv Super;Lise;7def les_utlaan(filnavn):
f = open(filnavn, 'r')
overskrift = f.readline().strip().split(';')
k = overskrift.index('dager')
tabell = []
for linje in f:
felt = linje.strip().split(';')
felt[k] = int(felt[k])
tabell.append(felt)
f.close()
return overskrift, tabell
overskrift, tabell = les_utlaan('utlaan.txt')
print(overskrift)
print(tabell)
print('Kolonnen «dager» ligger på plass', overskrift.index('dager'))Utskrift:
['tittel', 'låner', 'dager']
[['Sult', 'Ida', 14], ['Markens grode', 'Omar', 21], ['Naiv Super', 'Lise', 7]]
Kolonnen «dager» ligger på plass 2Tre ting skjer her, og alle tre er poenggivende.
Overskriftsraden leses med readline() før løkka, så for linje in f: starter på første datalinje. Kolonneindeksen k finnes ved navn, én gang, før løkka — ikke inni den. Og funksjonen returnerer to verdier som et tuppel, pakket ut i to variabler av kalleren (kap. 3.6).
Legg merke til variabelnavnet k. I dette kapitlet betyr k en kolonneindeks. I ordbokkapitlene betyr k en nøkkel. Samme bokstav, to helt ulike ting — se etter hva den brukes til.
Slik ville sensor sett på det: readline()-hoppet er ett poeng, det dynamiske kolonneoppslaget er et eksplisitt fullscore-krav, og konverteringen er et tredje. Hardkoder du felt[2] = int(felt[2]), mister du bare fullscore-punktet — resten står.
Bruk utlaan.txt fra eksempel 4:
Innholdet i utlaan.txt:
tittel;låner;dager
Sult;Ida;14
Markens grode;Omar;21
Naiv Super;Lise;7a) Hva skjer hvis du glemmer readline()-hoppet og prøver int(felt[2]) på hver linje?
b) Skriv les_kolonne(filnavn, navn) som returnerer verdiene i den kolonnen som heter navn, som en liste med strenger — uten å anta posisjonen.
c) Hva returnerer les_kolonne('utlaan.txt', 'sjanger'), og hvorfor?
Løkke 5 — Les fil til ordbok i stedet (~8 min)
En 2D-tabell er riktig når du skal gå gjennom alle radene. Skal du slå opp en bestemt rad — «hva var temperaturen på Kjevik?» — er en ordbok (kap. 3.5) bedre: da går oppslaget rett på, uten å lete.
Mønsteret er det samme som før helt til append-linja. Der bytter du ut å legge til en rad med å sette inn en nøkkel.
def les_ordbok(filnavn):
d = {}
f = open(filnavn, 'r')
for linje in f:
felt = linje.strip().split(separator)
d[felt[0]] = felt[1:]
f.close()
return dBlokken er en mal med en plassholder og er merket som ikke-kjørbar.
Det eneste nye er linja i midten, og den har to valg du må ta bevisst:
1. Hvilken kolonne er nøkkelen? Nesten alltid den første, men les oppgaven. Nøkler er unike — er det to linjer med samme nøkkel, overskriver den siste den første, og du mister en rad uten noen feilmelding.
2. Hva er verdien? Resten av raden (felt[1:]), ett enkelt felt (felt[1]), eller et konvertert tall. Velg det oppgaven faktisk trenger.
Les maalinger.txt inn i en ordbok fra stedsnavn til [temperatur, fuktighet], med tallene konvertert. Slå opp Kjevik.
maalinger.txt:Blindern;12.4;61
Kjevik;15.1;54
Værnes;9.8;73def les_ordbok(filnavn):
d = {}
f = open(filnavn, 'r')
for linje in f:
felt = linje.strip().split(';')
d[felt[0]] = [float(felt[1]), int(felt[2])]
f.close()
return d
maalinger = les_ordbok('maalinger.txt')
print(maalinger)
print(maalinger['Kjevik'])
print(maalinger['Kjevik'][0])
print('Blindern' in maalinger, 'Bergen' in maalinger)Utskrift:
{'Blindern': [12.4, 61], 'Kjevik': [15.1, 54], 'Værnes': [9.8, 73]}
[15.1, 54]
15.1
True FalseOppslaget maalinger['Kjevik'] går rett på, uten å gå gjennom de andre radene. Det er hele poenget med ordboka.
Slår du opp en nøkkel som ikke finnes, stopper programmet med KeyError. Derfor står in-testen på siste linje: den er den trygge måten å spørre «finnes denne?» på.
Slik ville sensor sett på det: valget mellom tabell og ordbok er sjelden feil i seg selv — det er begrunnelsen som teller. Skal du slå opp, velg ordbok. Skal du gå gjennom alt eller sortere, velg tabell.
Fila glass.txt har linjer på formen by;antall:
Innholdet i glass.txt:
Bergen;120
Tromsø;45
Stavanger;88a) Skriv les_glass(filnavn) som returnerer ordboka {by: antall} med antallet som heltall.
b) Skriv koden som finner samlet antall, ved å gå gjennom verdiene i ordboka.
c) Hva skjer med ordboka hvis fila får en fjerde linje Bergen;200?
Løkke 6 — with, og valget mellom de tre formene (~5 min)
Du har nå tre måter å ta imot en fil på, og de dekker alt eksamen spør om:
| Du skal … | Les fila som … |
|---|---|
| gå gjennom radene, sortere, finne beste | 2D-tabell |
| slå opp en bestemt nøkkel | ordbok |
| telle tegn eller ord, snu teksten | én streng med .read() |
Til slutt en variant av selve åpningen, som du vil se i andre bøker og i noen løsningsforslag.
Åpner fila og lukker den automatisk når blokken er ferdig — også hvis det skjer en feil underveis.
with open('maalinger.txt', 'r') as f:
tabell = []
for linje in f:
tabell.append(linje.strip().split(';'))
print(len(tabell))
print(f.closed)Utskrift:
3
Truef.closed er sann etterpå: fila er lukket uten at du skrev close().
Bokas hovedform er likevel open … close, fordi det er den formen løsningsforslagene i arkivet bruker, og fordi den gjør de to stegene synlige for sensor. with er fullt gyldig og gir full pott — bruk den formen du husker best under press, men bland dem aldri i samme funksjon.
Et bibliotek lagrer utlånene sine i utlaan.txt, med en overskriftsrad:
Innholdet i utlaan.txt:
tittel;låner;dager
Sult;Ida;14
Markens grode;Omar;21
Naiv Super;Lise;7a) Skriv les_utlaan(filnavn) som hopper over overskriftsraden og returnerer en 2D-tabell der dager er et heltall og de øvrige feltene er strenger. Kolonnerekkefølgen skal ikke antas kjent.
b) Skriv lengste_laan(tabell, overskrift) som returnerer tittelen på boka med flest dager, uten å bruke max.
c) Hva returnerer funksjonen i b) hvis tabellen er tom, og hvorfor er det et bevisst valg?
Fila maalinger.txt inneholder:
Blindern;12.4;61
Kjevik;15.1;54
Værnes;9.8;73Hva skriver programmet ut?
f = open('maalinger.txt', 'r')
tekst = f.read()
f.close()
linjer = tekst.strip().split('\n')
print(len(linjer))
d = {}
for linje in linjer:
felt = linje.split(';')
d[felt[0]] = int(felt[2])
print(d['Værnes'] - d['Kjevik'])
print(sorted(d))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 Norges teknisk-naturvitenskapelige universitet. Dette er ikke offisielt studiemateriell. Les mer.