Lektion 6 · Cirka 45 min genomgång

Filer och I/O
File I/O

Variabler försvinner när ett program avslutas. Filer gör det möjligt att lagra data beständigt, läsa in den i en senare körning och utbyta strukturerad information med andra program.

with använder en context manager. Filen stängs när blocket lämnas, även om ett undantag uppstår inne i blocket.

namn.pyPython
with open("names.txt", "a", encoding="utf-8") as file:
    file.write("Ada\n")

Före genomgången

Se Harvard lecture 6 och skapa en arbetsmapp

Den här kursen hoppar över Harvardavsnitt 5 om unit tests. Därför följer lektion 06 direkt efter lektion 04. Numret behålls så att filmer, notebook och Harvarduppgifter fortfarande är lätta att hitta.

2

Skapa en mapp

Skapa lektion_06 i kursmappen och öppna hela mappen i VS Code.

3

Skapa provdata

Skapa names.txt och students.csv. Filerna ska ligga i samma arbetsmapp som programmen.

Notebooken är ett utforskande kursunderlag

Den nedladdningsbara notebooken innehåller lärarens ursprungliga exempel. Guiden kompletterar dem med bland annat encoding="utf-8", newline="" för CSV och dagens exakta Harvardkrav.

Dagens karta

Från flyktiga värden till beständig data

Vi börjar med vanliga textfiler, går vidare till strukturerade CSV-filer och avslutar med binära bildfiler.

programminnevärden under körningenfilobjektöppet flödetext eller bytesrepresentationlagringsenhetbeständig fil
01

Varför filer?

I/O flyttar data mellan programmet och omvärlden

I/O betyder input/output. Filinput innebär att programmet läser data från en fil. Filoutput innebär att programmet skriver eller skapar en fil.

Flyktigt

Objekt i arbetsminnet

En list, ett dict och andra objekt finns normalt bara medan processen körs. När processen avslutas försvinner objekten ur dess minne.

Beständigt

Data i en fil

En fil lagras utanför programmets arbetsminne. Ett senare program kan öppna samma fil och tolka innehållet på nytt.

Beständig betyder inte oförstörbar

Filer kan skrivas över, raderas, bli korrupta eller vara otillgängliga. Ett program måste därför välja rätt filläge och hantera rimliga felvägar.

02

Öppna en resurs

open returnerar ett filobjekt

Ett filobjekt representerar en öppen förbindelse till en fil. Det håller reda på bland annat filläge, teckenkodning och aktuell position.

grundform.pyPython
file = open(
    "names.txt",
    "r",
    encoding="utf-8",
)
print(file.read())
file.close()

Tre centrala argument

Filnamn eller sökväg
En str eller ett path-like objekt som pekar ut resursen.
Mode
En sträng som anger om filen ska läsas, skrivas, läggas till eller hanteras binärt.
encoding
Reglerna för hur tecken översätts till bytes. Använd normalt "utf-8" för text.
"r"read · standardläge · filen måste finnas
"w"write · skapar fil eller raderar befintligt innehåll
"a"append · skapar fil eller lägger till sist
"x"exclusive create · misslyckas om filen redan finns
"b"binary · kombineras exempelvis som "rb" och ger bytes
"w" tömmer en befintlig fil omedelbart

Öppna därför inte en värdefull fil i write-läge bara för att undersöka den. Använd "r" när programmet endast ska läsa.

Relativa sökvägar utgår normalt från aktuell arbetskatalog

open("data/names.txt") letar relativt den katalog där processen startades, inte nödvändigtvis katalogen där Pythonfilen ligger. VS Codes öppna mapp och terminalens aktuella katalog påverkar därför resultatet.

03

Context manager

with stänger filen även när blocket lämnas genom ett fel

En context manager hanterar start och avslutning av en resurs. För ett filobjekt innebär det att filen öppnas före blocket och stängs när blocket lämnas.

Manuellt

close kan glömmas

manuellt.pyPython
file = open("names.txt", "a", encoding="utf-8")
file.write("Ada\n")
file.close()

Om en operation mellan open och close skapar ett undantag kanske sista raden aldrig nås.

Rekommenderat

with avgränsar livstiden

context.pyPython
with open("names.txt", "a", encoding="utf-8") as file:
    file.write("Ada\n")

Efter det indragna blocket är filen stängd. Namnet file kan fortfarande finnas, men objektet får inte längre läsas eller skrivas.

as file är en vanlig lokal bindning

Namnet file är inte ett reserverat ord. Det refererar till filobjektet inne i koden och kan ersättas med ett tydligare namn som csvfile.

04

Sekventiell text

En textfil läses som str och innehåller även radbrytningar

I textläge avkodar Python filens bytes till tecken. Parametern encoding="utf-8" gör valet uttryckligt och ger samma avkodning på olika datorer.

las_namn.pyPython
with open("names.txt", encoding="utf-8") as file:
    for line in file:
        name = line.rstrip("\n")
        print(f"Hej, {name}!")
ResultatHej, Ada!
Hej, Grace!

Iteration läser en rad i taget

Filobjektet är itererbart. Varje varv ger nästa rad som en str. Modellen behöver inte läsa hela filen till minnet på en gång.

Textens radbrytning ingår normalt sist i raden. rstrip("\n") tar bara bort radbrytningstecknet. rstrip() utan argument tar bort alla avslutande whitespace-tecken, även betydelsefulla blanksteg.

Hela innehållet

read()

Returnerar återstående text som en enda str. Praktiskt för små filer, men stora filer kan ta mycket minne.

Alla rader

readlines()

Returnerar en list av rader. Radbrytningarna behålls normalt i elementen.

skriv_namn.pyPython
name = input("Namn: ")

with open("names.txt", "a", encoding="utf-8") as file:
    file.write(f"{name}\n")
write lägger inte till en radbrytning automatiskt

Utan \n hamnar nästa namn direkt efter det föregående. Alternativet print(name, file=file) använder däremot print-funktionens vanliga radslut.

05

Rader och fält

CSV är text med en överenskommen tabellstruktur

CSV betyder comma-separated values. En rad representerar normalt en post och fälten representerar kolumner. Formatet tillåter att ett fält med kommatecken omges av citattecken.

students.csvPython
name,home
Harry,"Number Four, Privet Drive"
Ron,The Burrow

Varför split(",") är skört

En vanlig str.split känner inte till CSV-regler. Den delar även vid kommatecknet inne i den citerade adressen och skapar tre delar i stället för två.

Standardmodulen csv tolkar avgränsare, citattecken och andra CSV-detaljer. Vi ska använda formatets parser, inte återskapa den.

Positionsbaserat

csv.reader

reader.pyPython
import csv

with open("students.csv", newline="", encoding="utf-8") as file:
    reader = csv.reader(file)
    for row in reader:
        print(row)

Varje rad blir en list av str-värden. row[0] betyder första kolumnen.

Rubrikbaserat

csv.DictReader

dict_reader.pyPython
import csv

with open("students.csv", newline="", encoding="utf-8") as file:
    reader = csv.DictReader(file)
    for row in reader:
        print(row["name"])

Första raden används som nycklar. Varje följande rad blir ett dict, så koden kan använda kolumnnamn.

CSV-värden är normalt str

csv.reader omvandlar inte automatiskt "42" till int. Programmet måste själv omvandla fält när datamodellen kräver det.

Öppna CSV-filer med newline=""

Dokumentationen rekommenderar detta så att csv-modulen själv får hantera radslut korrekt på olika operativsystem.

06

Bearbeta och skriv strukturerat

key talar om vilket värde sorted ska jämföra

Ett dict har flera värden. När en list av sådana poster ska sorteras behöver sorted en funktion som hämtar sorteringsvärdet ur varje post.

Namngiven funktion

Ett tydligt första steg

sortera.pyPython
def get_name(student):
    return student["name"]

for student in sorted(students, key=get_name):
    print(student["name"])

sorted anropar get_name en gång per post och använder returvärdet vid jämförelsen.

lambda

En namnlös funktion med ett uttryck

lambda.pyPython
for student in sorted(
    students,
    key=lambda student: student["name"],
):
    print(student["name"])

Syntaxen är lambda parameter: uttryck. Uttryckets värde returneras automatiskt. Använd formen för en kort, lokal funktion.

key får funktionen — inte resultatet av ett för tidigt anrop

Skriv key=get_name, inte key=get_name(). sorted ansvarar för att anropa funktionen med varje element.

skriv_csv.pyPython
import csv

fields = ["name", "home"]

with open(
    "students.csv",
    "w",
    newline="",
    encoding="utf-8",
) as file:
    writer = csv.DictWriter(file, fieldnames=fields)
    writer.writeheader()
    writer.writerow({
        "name": "Ron",
        "home": "The Burrow",
    })

DictWriter behöver ett schema

fieldnames anger kolumnernas namn och ordning. writeheader() skriver rubrikraden. writerow tar ett dict och placerar värdena i rätt kolumner.

Välj "w" när filen ska byggas på nytt och "a" när nya poster verkligen ska läggas sist. I append-läge måste programmet undvika att skriva rubriken på nytt vid varje körning.

07

Inte bara text

Binära filer innehåller bytes som ett format måste tolka

Bilder, ljud och många dokument är binära format. I binärt läge arbetar Python med bytes, inte str. Ofta använder vi ett specialiserat package som känner till formatets struktur.

Pillow installeras — PIL importeras

Pillow är distributionens namn på PyPI. Den moderna koden importerar fortfarande genom namnrymden PIL. Installera i kursens aktiva miljö med python -m pip install Pillow.

Image.open avkodar ett bildformat till ett bildobjekt. ImageOps.fit returnerar en ny bild som både skalats och beskurits till önskad storlek. Returvärdet måste sparas.

bild.pyPython
from PIL import Image, ImageOps

with Image.open("photo.jpg") as photo:
    fitted = ImageOps.fit(photo, (600, 600))
    fitted.save("square.jpg")
bildfilkomprimerade bytesImage.openavkodar formatbildobjektbearbetas i minnetsavekodar ny fil
ImageOps.fit ändrar inte originalobjektet på plats

ImageOps.fit(photo, size) returnerar den bearbetade bilden. Om returvärdet ignoreras fortsätter variabeln photo att referera till den ursprungliga bilden.

Problem Set 6

Fyra övningar som bygger en sammanhängande filkedja

Harvards aktuella uppgiftssidor är kravspecifikationen. Testa alltid normala fall, gränser, felaktigt antal argument, fel filtyp och saknad fil.

01

Lines of Code

Ta exakt ett kommandoradsargument som slutar på .py. Räkna rader som inte är tomma och inte börjar med # efter valfritt inledande whitespace. En docstring räknas som kod. Avsluta kontrollerat vid fel antal argument, fel filtyp eller saknad fil.

Aktuell uppgift ↗
02

Pizza Py

Ta exakt ett CSV-filnamn som argument, läs tabellen med csv och visa den med tredjepaketet tabulate och formatet "grid". Validera argument, ändelsen .csv och att filen finns.

Aktuell uppgift ↗
03

Scourgify

Ta en inputfil och en outputfil som två kommandoradsargument. Läs name,house med DictReader, dela "last, first" och skriv first,last,house med DictWriter och en rubrikrad.

Aktuell uppgift ↗
04

CS50 P-Shirt

Ta input- och outputbild som två argument. Tillåt .jpg, .jpeg och .png utan hänsyn till skiftläge, kräv samma extension, använd ImageOps.fit till tröjans storlek, klistra in tröjan med dess transparensmask och spara resultatet.

Aktuell uppgift ↗
Äldre lokala lösningsskisser är inte facit

I några äldre filer används interaktiv input där uppgiften kräver sys.argv, en CSV-rubrik saknas och returvärdet från ImageOps.fit ignoreras. Utgå från den aktuella uppgiftstexten och använd skisserna endast för diskussion och felsökning.

Fördjupa och repetera

Resurser för lektion 6

Börja med kursguiden. Använd sedan originaldokumentationen när du behöver exakta argument, returvärden eller undantag.

Efter lektionen kan jag …

skilja data i minnet från beständig data i en fil

välja rätt filläge och förklara risken med "w"

använda with, UTF-8 och säkra relativa sökvägar

läsa och skriva text utan felaktiga radbrytningar

läsa CSV som list eller dict

förklara lambda och key vid sortering

skriva CSV med rubrik och bearbeta en binär bild med Pillow