L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
Den klassiska programmodellen
Introduktion till programmering
D0009E
Hur kan data överleva en programkörning?
indata
Föreläsning 11: “Filer och undantag”
utdata
indata
Program A
utdata
Program B
Filsystem
tid
1
2
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
Interaktiva program
Filsystem
Består av en mängd filer organiserade i en ofta
hierarkisk struktur
Hur kan data överleva en programkörning?
indata
utdata
indata
INTRODUKTION TILL PROGRAMMERING
D0009E
utdata
Varje fil har ett namn och ett innehåll (samt eventuella
övriga detaljer såsom läs- och skrivrättigheter)
Interna noder i filstrukturen kallas kataloger
(directories) eller mappar (folders) – ej att förväxla
med Pythons inbyggda datatyp kataloger (dictionaries)
Filsystem
En fils plats i filstrukturen bestäms av namnet. Exempel:
/usr/share/dict/words
betyder filen words i katalogen dict som ligger i
katalogen share i katalogen usr på systemets toppnivå
tid
3
4
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
Filer
Filobjekt
Filer kan betraktas som böcker:
• De måste öppnas före de kan läsas eller skrivas
• De läses (eller skrivs) oftast från pärm till pärm, men
det är också möjligt att hoppa omkring på andra vis
• Att blanda läsning med skrivning är mer komplicerat
än att bara läsa (eller bara skriva)
• Allra sist ska de stängas
Resultatet av en lyckad öppning är ett filobjekt – en
intern datastruktur som fungerar som "handtag" till den
egentliga filen
För att skriva data används filobjektets write-metod:
f.write("Now is the time")
f.write("to close the file")
Efter stängning blir filen tillgänglig för andra att läsa
f.close()
Att öppna filen test.dat för skrivning:
f = open("test.dat", "w")
Att öppna filen test.dat för läsning:
f = open("test.dat", "r")
Denna operation skapar filen test.dat om den inte redan
finns, i annat fall krymps den till storlek 0
5
INTRODUKTION TILL PROGRAMMERING
D0009E
Denna fil måste finnas, annars sker ett runtime-fel
6
1
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
Filläsning
INTRODUKTION TILL PROGRAMMERING
D0009E
Filkopiering
Metoden read ger filens hela innehåll som en sträng:
>>> text = f.read()
>>> print text
Now is the timeto close the file
>>> f.close()
Alternativ read där antalet önskade tecken också anges:
>>> f = open("test.dat", "r")
>>> print f.read(5)
Now i
>>> print f.read(1000006)
s the timeto close the file
>>> print f.read(),
>>>
7
Kopierar filen oldFile till filen newFile
def copyFile(oldFile, newFile):
f1 = open(oldFile, "r")
f2 = open(newFile, "w")
while True:
text = f1.read(50)
if text == "":
break
f2.write(text)
f1.close()
f2.close()
OBS: ny sats break – bryter närmast yttre snurra
8
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
Filtyper
INTRODUKTION TILL PROGRAMMERING
D0009E
Textfiler
De flesta filer i Unix-baserade filsystem är textfiler, dvs
innehållet är bitmönster som representerar text enligt
samma kodning som Python använder för strängar
Textfiler kan i sin tur ha olika struktur och syntax – en
fil kan vara Python programtext, en annan ett
epostmeddelande, en tredje en webbsida i html, etc
Filer som inte är textfiler är antingen rena
maskinkodfiler (körbara program) eller s k binära
filer enligt något annat specialiserat dataformat
(exempelvis bilder i jpeg-format, filmer i mpeg-format).
Binära filer kommer inte att behandlas ytterligare i
denna kurs.
Ofta (men inte alltid) avspeglas filtypen i filnamnet:
Python-filer (.py), mpeg-filer (.mpeg), etc
9
Underliggande struktur som går igen i alla textfiler
(oberoende av format): en textfil är organiserad som en
följd av rader
En ny rad markeras av att ASCII-tecknet nyrad ('\n')
ligger insprängt i texten
Låt oss skapa en textfil med tre rader:
f = open("test.dat", "w")
f.write("line one\nline two\nline three\n")
f.close()
Vi förstår det som att varje rad slutar med tecknet '\n',
utom möjligtvis filens sista rad
10
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
Radläsning
Radläsning
Filmetoden read() ger som sagt hela innehållet som en
sträng. Ofta kan det vara bättre att läsa en rad i taget:
>>> f = open("test.dat", "r")
>>> print f.readline()
line one
Metoden readlines() ger filens (kvarvarande) innehåll
som en lista av rader:
>>> f = open("test.dat", "r")
>>> print f.readlines()
['line one\012', 'line two\012', 'line three\012']
>>> print f.readlines()
[]
>>> print f.readline() + f.readline()
line two
line three
>>> print f.readline()==""
True
11
Observera: print snyggar bara till sådana strängar som
anges direkt som argument (tar bort ", skriver riktiga
nyrad, etc). Strängar som ingår i sammansatta data (som
listor) skrivs ut på samma sätt som de läses in i Python
(Av historiska skäl skrivs \-koderna här med basen 8)
12
2
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
Filkopiering med filtrering
Kopiera oldFile, men skippa rader som inleds med #
def filterFile(oldFile, newFile):
f1 = open(oldFile, "r")
f2 = open(newFile, "w")
while True:
text = f1.readline()
if text=="":
break
if text[0]=='#':
continue
f2.write(text)
f1.close()
f2.close()
Ny sats: continue – hoppar till nästa varv i en snurra
13
INTRODUKTION TILL PROGRAMMERING
D0009E
Filkopiering med filtrering
Alternativ formulering, nu med for-snurra:
def filterFile(oldFile, newFile):
f1 = open(oldFile, "r")
f2 = open(newFile, "w")
for text in f1:
if text[0] != '#':
f2.write(text)
f1.close()
f2.close()
Filer kan alltså också betraktas som sekvenser på samma
sätt som listor, strängar och tupler. Elementen i en filsekvens är filens rader
14
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
Textformatering
Formateringsoperatorn %
Att ha möjligheten att skriva text är gott och väl, men
ska andra typer av värden sparas på textfiler måste de
först konverteras till strängar
Detta kan göras med den inbyggda konverteringsfunktionen str() kombinerat med konkatenering:
num = 73
value = 3.14
f.write( "Row " + str(num) + ": = " + str(value) )
Python erbjuder dock ett mycket behändigt alternativ:
operatorn % i en ny betydelse som formateringsoperation!
(% betyder som bekant modulus när den appliceras på heltal)
Operatorn % applicerad på en strängmall och en
tupel med värden producerar en kopia av mallen där
markerade "hål" har ersatts med värden från tupeln:
15
'abchål1defhål2gh...ijhålNklm'
%
'abcv1defv2gh...ijvNklm'
Underförstått: v1, v2, ..., vN konverteras till strängformat
innan de sätts in på motsvarande plats i mallen
16
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
Hål
En ny form av escape-sekvens (hanteras dock i run-time)
• %d
heltalsvärde
• %f
flyttalsvärde
• %s
strängvärde
Exempel (notera tecknet som möjliggör udda radbrytning):
>>> 'In %d days we made %f million %s' \
%
\
(34, 6.1, 'dollars')
'In 34 days we made 6.100000 million dollars'
Som vi ser skrivs flyttal ut med 6 decimaler (default)
17
( v1, v2, ..., vN )
INTRODUKTION TILL PROGRAMMERING
D0009E
Mer om formateringsoperatorn
Felaktig användning:
>>> '%d %d %d' % (1, 2)
TypeError: not enough arguments for format string
>>> '%d' % 'dollars'
TypeError: int argument required
Ok, om än lite förvirrande:
>>> '%s' % 37
'37'
Underförstådd betydelse av föregående exempel:
>>> '%s' % str(37)
'37'
18
3
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
Ytterligare kontroll av formatet
Exempel
Önskat minsta antal tecken (fyll ut med mellanslag):
>>> '%6d' % 62
' 62'
>>> '%-6d' % 62
'62 '
>>> '%12f' % 6.1
' 6.100000'
Funktion som skriver ut en lönerapport (given som ett
dictionary) i en tabell med två kolumner:
def report(wages, file):
f = open(file, "w")
people = wages.keys()
people.sort()
for x in people:
f.write( '%-20s %12.2f' % (x, wages[x]) )
f.close()
Önskat antal decimaler för flyttal:
>>> '%12.2f' % 6.1
'
6.10'
19
Notera: koden är egentligen inte beroende av att det är
just namn och löner som skrivs ut – alla dictionaries som
mappar strängar till flyttal är ok!
20
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
Exempel
Sammansatta data
Testkörning:
wages = {'mary' : 10.23, 'joe' : 5.45, 'josh' : 4.25}
report(wages, "test.dat")
Genererat innehåll i filen test.dat:
joe
5.45
josh
4.25
mary
10.23
Rapporten ser ok ut så länge namnen är kortare än 20
tecken och lönerna har färre än 9 siffror + 2 decimaler
Alltså, formatsträngen '%-20s %12.2f'
21
Tyvärr fungerar formateringsoperatorn bara för de
primitiva typerna, ej för sammansatta data som listor
Här får i stället funktionen str() användas:
f.write( str([1,2,3]) )
f.write( str({'x' : 2, 'y' : 3}) )
Ett intressant problem uppstår dock om vi vill kunna
läsa in filen f vid ett senare tillfälle, och återskapa de
sammansatta värdena. Betrakta den genererade texten:
'[1,2,3]{'x': 2, 'y': 3}'
Hur ska vi angripa en sådan sträng? Tecken för tecken?
Hur vet vi var ett värde slutar och ett annat börjar?
22
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
Modulen pickle
Lösning: om vi mest är intresserade av att kunna spara
undan data på fil för att sedan kunna läsa in dem, och
inte har några åsikter om hur den genererade texten
egentligen ser ut, så erbjuder Python modulen pickle
Pickle betyder (salt-)inläggning, och det är så vi ska se
den sparade texten: som en inläggning av godtyckliga
data för att "öka hållbarheten" mellan programkörningar
Skriver en textkodning av värdet x på filen f:
pickle.dump( x, f )
Avkodar nästa värde i filen f och returnerar det:
x = pickle.load( f )
23
INTRODUKTION TILL PROGRAMMERING
D0009E
INTRODUKTION TILL PROGRAMMERING
D0009E
Exempel
Kod som lägger in data i filen test.dat:
import pickle
f = open("test.dat", "w")
pickle.dump( [1,2,3], f )
pickle.dump( {'x': 2, 'y': 3}, f )
f.close()
Kod (kanske i ett annat program) som läser in dessa
data:
import pickle
f = open("test.dat", "r")
list = pickle.load( f )
dict = pickle.load( f )
f.close()
24
4
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
Runtime-fel vi stött på
INTRODUKTION TILL PROGRAMMERING
D0009E
Runtime-fel vi stött på
Division med 0
>>> print 55/0
ZeroDivisionError: integer division or modulo
Försök att öppna en icke existerande fil:
>>> f = open("I.dont.exist", "r")
IOError: [Errno 2] No such file: 'I.dont.exist'
Indexering utanför en lista:
>>> a = []
>>> print a[5]
IndexError: list index out of range
Referens av odefinierad variabel:
>>> print kalle
NameError: name 'kalle' is not defined
Uppslagning av obefintlig nyckel i ett dictionary:
>>> b = {}
>>> print b['what']
KeyError: what
25
Anrop av funktion med fel antal argument:
>>> def f(x,y):
return x+y
>>> print f(3)
TypeError: f() takes exactly 2 arguments (1 given)
26
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
Undantag
Undantag
Alla dessa runtime-fel är exempel på s k undantag
(exceptions), och de karaktäriseras av ett namn
(TypeError, IndexError, etc) och en beskrivande text
Om hanterar undantaget
själv
Normalt beteende då ett undantag uppstår:
•avsluta programkörningen
•skriv ut information om undantaget
• Istället
Det är dock möjligt att hantera undantag själv också,
med hjälp av try-satsen:
try:
satslista
except namn:
Kan upprepas för olika namn
satslista
27
• programmet avslutas inte
• inget skrivs ut på skärmen
• kör koden efter except
namn
• är tänkt att ”ta hand” om
felet
try:
satslista
except namn1:
satslista1
except namn2:
satslista2
.
.
.
except namn:
satslista
• Hantera felet
• upplysa användaren
• göra nåt annat lämpligt
28
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
Exempel
Hantering av det fall att en namngiven fil inte finns:
filename = raw_input('Enter a file name: ')
try:
f = open(filename, "r")
except IOError:
print "There is no file named", filename
Variant på idén, nu som funktion:
def exists(filename):
try:
f = open(filename)
f.close()
return True
except IOError:
return False
29
INTRODUKTION TILL PROGRAMMERING
D0009E
INTRODUKTION TILL PROGRAMMERING
D0009E
Att generera undantag
Använd satsen raise namn, beskrivning:
def inputNumber():
x = input('Pick a number: ')
if x == 17:
raise ValueError, '17 is a bad number'
return x
Vid körning:
>>> inputNumber()
Pick a number: 17
ValueError: 17 is a bad number
>>>
30
5
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
INTRODUKTION TILL PROGRAMMERING
D0009E
L U L E Å T E K N I S K A U N I V E R S I T ET
SY S T E M T E K N I K
Att generera undantag
En funktion som ger undantag
•returnerar inget värde!
• ger undantaget istället för värdet
• Undantag kan betraktas som ett ”sidospår” i
exekveringen
•när vi inte tycker att det ”går” att returnera nåt
•vi slipper hitta på ett värde att returnera (t.ex. -1)
• Bra system för att hantera fel i program
•kasta undantag i funktion som gått fel
•fånga i funktionen som anropade
• inget returvärde
31
INTRODUKTION TILL PROGRAMMERING
D0009E
Att generera undantag
Undantaget NotImplementedError är lämpligt att
använda om man vill definiera halvfärdiga funktioner:
def myFun(x, y):
if x > y:
return x-y
else:
raise NotImplementedError, 'myFun'
Övriga fördefinierade undantag kan också användas i
raise, och det är t o m möjligt att skapa egna namn
Undantag genererade med raise kan naturligtvis också
fångas med try (vanligt i större program)
32
6