CMD + K

Informasjonsteknologi grunnkursFilbehandling og unntakBegreper & formler5
6 min lesing1 video

Filbehandling og unntak

Programmer trenger ofte å lese og skrive filer for å ta vare på data. Vi åpner filer med open og with, leser og skriver innhold, og lukker dem trygt. Samtidig lærer vi å håndtere feil som oppstår underveis med try/except.

Læringsmål
  • 01Åpne, lese og skrive filer med open og de tre modusene r, w og a, og forklare hvorfor en fil må lukkes
  • 02Bruke with-blokken til å håndtere filer trygt og forklare hvorfor den er å foretrekke fremfor manuell close
  • 03Forklare hva et exception er, gi eksempler på vanlige typer, og beskrive hva som skjer når et unntak ikke fanges
  • 04Fange forventede feil med try/except for en bestemt unntakstype og bygge en løkke som hopper over ugyldige data i stedet for å krasje

Når noe utenfor programmet kan svikte

Så langt har programmene dine levd i sin egen lille verden: alt de trengte, lå allerede i variabler. Men ekte programmer må snakke med omverdenen — lese en fil brukeren har laget, ta imot et tall noen taster inn, hente data fra et nettverk. Felles for alt dette er at det kan gå galt på måter du ikke rår over. Filen finnes kanskje ikke. Brukeren skriver «tre» når du ba om et tall. Disken er full. Dette kapittelet handler om to ting som hører sammen: hvordan du leser og skriver filer, og hvordan du fanger opp feil i stedet for å la programmet krasje.

La oss begynne med et eksempel på hva som skjer uten beskyttelse. Tenk deg at du ber brukeren om alder og regner ut fødselsår:

python
alder = int(input("Hvor gammel er du? ")) print(2026 - alder)

Skriver brukeren 30, går alt fint. Skriver hen tretti, stopper programmet brått med en ValueError og en uleselig feilmelding. For brukeren ser det ut som om alt krasjet. Vi vil heller fange feilen og be om et nytt forsøk. Det er nettopp det unntakshåndtering gir oss.

Filer: åpne, lese, lukke

En fil på disken må open-es før du kan bruke den. Funksjonen open tar et filnavn og en modus som sier hva du vil gjøre: "r" for å lese (read), "w" for å skrive (write, overskriver alt som var der), og "a" for å legge til på slutten (append). Når du er ferdig, må filen lukkes med close, ellers risikerer du at det du skrev aldri kommer trygt ned på disken.

python
fil = open("notat.txt", "w") fil.write("Hei\n") fil.close()

Problemet med dette mønsteret er at hvis noe krasjer mellom open og close, blir filen aldri lukket. Derfor finnes en tryggere form som lukker filen automatisk — også hvis det oppstår en feil underveis.

with-blokken — lukker for deg

Nøkkelordet with åpner en fil og garanterer at den lukkes når du forlater blokken, uansett om du forlot den normalt eller fordi en feil oppstod. Dette er den anbefalte måten å jobbe med filer på, og den du bør venne deg til fra første stund:

python
with open("notat.txt", "w") as fil: fil.write("Linje 1\n") fil.write("Linje 2\n") # her er filen allerede lukket — du trenger ikke close()

For å lese og skrive en fil leser du den på en av flere måter. fil.read() gir hele innholdet som én streng. fil.readlines() gir en liste der hvert element er én linje. Aller vanligst er å gå rett gjennom filen med en for-løkke, som gir deg én linje om gangen uten å laste hele filen i minnet samtidig:

python
with open("notat.txt", "r") as fil: for linje in fil: print(linje.strip()) # strip() fjerner linjeskiftet på slutten

Legg merke til strip(). Hver linje du leser fra en fil har et usynlig linjeskift (\n) på slutten, og det vil du nesten alltid fjerne før du behandler teksten. Dette er en så vanlig kilde til forvirring at det er verdt å huske: tekst fra filer og fra input kommer ofte med ekstra blanktegn du må rydde bort.

Unntak — når ting går galt

Et exception (unntak) er Pythons måte å si fra om at noe gikk galt under kjøring. Du har allerede møtt flere: ValueError når int("abc") ikke lar seg konvertere, KeyError når en dictionary-nøkkel mangler, ZeroDivisionError ved deling på null, og FileNotFoundError når en fil ikke finnes. Når et unntak oppstår og ingen fanger det, stopper programmet og skriver ut en traceback — den lange feilmeldingen som peker tilbake til linja der det smalt.

Poenget er ikke å unngå alle unntak — det kan du ikke — men å bestemme hva som skal skje når de inntreffer. Skal programmet stoppe? Skrive en pen feilmelding? Spørre brukeren på nytt? Det styrer du med try/except.

try og except

En try-blokk inneholder koden som kan feile. En except-blokk inneholder det som skal skje hvis den feiler. Kjører try-blokken uten feil, hoppes except over helt. Oppstår det et unntak, avbrytes try-blokken umiddelbart, og Python hopper til except:

python
try: alder = int(input("Hvor gammel er du? ")) print("Fødselsår omtrent:", 2026 - alder) except ValueError: print("Det var ikke et tall. Prøv igjen.")

Skriver brukeren tretti, krasjer ikke lenger programmet — i stedet skrives en vennlig beskjed. Legg merke til at vi fanger en bestemt type unntak, ValueError. Det er god skikk: fang det du faktisk forventer, ikke alt mulig. En naken except: som fanger alt skjuler også feil du ikke hadde tenkt på, og gjør programmet vanskeligere å feilsøke.

Vil du gjenta forsøket til brukeren faktisk skriver et tall, pakker du try/except inn i en while-løkke. Lykkes konverteringen, bryter du ut med break; feiler den, sier except fra og løkken kjører en runde til. Dette mønsteret — løkke rundt et try med break ved suksess — er selve standardmåten å lese gyldig input på, og du vil kjenne det igjen i utallige programmer:

python
while True: try: alder = int(input("Hvor gammel er du? ")) break except ValueError: print("Det var ikke et tall. Prøv igjen.")
janeistartkjør try-blokkenoppstod et unntak?kjør except-blokkenfortsett programmetsluttGikk try-blokken bra, hopper vi forbi except og rett til fortsettelsen. Oppstod et unntak, tar vi om innom except først. Begge veier samles igjen, og programmet lever videre.
FIGtry/except: to veier, samme sluttpunkt

Figuren viser flyten. Først forsøkes konverteringen. Lykkes den, fortsetter vi rett ned forbi except. Feiler den, forgrener vi av til feilhåndteringen. Uansett hvilken vei vi gikk, samles trådene igjen etterpå, og programmet lever videre.

Du kan utvide mønsteret med flere ledd. En except-blokk per unntakstype lar deg reagere ulikt på ulike feil. En else-blokk kjører bare hvis try gikk bra. Og en finally-blokk kjører alltid, uansett utfall — nyttig til opprydding som må skje uansett:

python
try: tall = int(verdi) except ValueError: print("Ikke et tall") else: print("Det gikk bra, tallet er", tall) finally: print("Dette skrives uansett")

Sett det sammen: robust prosessering

Tenk deg at du har en liste med tekstlinjer der hver linje skal være et tall, men noen er rotete. Du vil summere de gyldige og hoppe over resten uten å krasje.

try-except

Robust prosessering: hopp over ugyldige linjer

python
# Rådata som tekstlinjer (samme idé som linjer lest fra en fil):linjer = ["42", "17", "tretti", "  8 ", "", "100"] sum_gyldige = 0antall_feil = 0 for linje in linjer:    try:        tall = int(linje.strip())   # strip() rydder blanktegn først    except ValueError:        antall_feil += 1            # ikke et tall — hopp over, ikke krasj        continue    sum_gyldige += tall print("Sum av gyldige tall:", sum_gyldige)   # 167print("Ugyldige linjer hoppet over:", antall_feil)   # 2

Hver linje forsøkes konvertert inni en try; en ugyldig linje fanges av except ValueError og hoppes over med continue i stedet for å velte hele beregningen. Akkurat dette mønsteret bruker du når du leser linjer fra en fil — bare at dataene her ligger i en liste i minnet i stedet for på disk.

viser nettopp dette: en løkke der hver linje forsøkes konvertert inni en try, og en ugyldig linje fanges og hoppes over i stedet for å velte hele beregningen. Dette er et mønster du vil bruke igjen og igjen når du behandler data utenfra — der du aldri kan stole på at hvert eneste element er på formen du håpet.

Merk at snutten jobber på en liste av strenger i minnet, ikke på en virkelig fil. Tankegangen er nøyaktig den samme som når du leser linjer fra en fil: les inn rådata, forsøk å tolke hver bit, fang det som ikke lar seg tolke. Forskjellen er bare hvor dataene kommer fra. Vi teller også opp hvor mange linjer som ble forkastet, slik at programmet kan si fra om at noe var galt med inndataene — det er ofte like nyttig å vite at to linjer ble hoppet over som å få selve summen.

Hvorfor dette gjør programmene dine voksne

Et program som krasjer på første rare input er skjørt. Et program som forventer at ting kan gå galt, og håndterer det rolig, er robust. Å skille mellom de to handlingene — lese fra omverdenen og forberede seg på at den svikter — er det som skiller en øvelse fra noe folk faktisk kan bruke. Bruk with for å håndtere filer trygt, fang de unntakene du forventer med try/except, og vær spesifikk om hvilke feil du fanger. Da tåler programmet ditt den uryddige virkeligheten det skal leve i.

Mine notater

Skriv egne notater for dette kapittelet. De lagres automatisk og dukker opp i «Mine notater»-oversikten paa fag-siden.

Laster…