Werken met bestanden

Inleiding

In dit hoofdstuk leer je over het werken met bestanden. Werken met bestanden zul je vaak doen in je programma’s. Een bestand kan van alles zijn, maar vaak zal het gaan om tekstbestanden. Daar ligt dan ook de focus op in dit hoofdstuk.

Leerdoelen

Aan het einde van dit hoofdstuk:

  • Begrijp je hoe je een bestand opent en leest

  • Begrijp je hoe je naar een bestand schrijft

Werken met bestanden

Een bestand kan van alles zijn. Een eenvoudig tekstbestand, een Wordbestand, een afbeelding, een CSV-bestand, et cetera. Een bestand kan lokaal zijn opgeslagen, maar het kan net zo goed ergens op het internet staan.

Het is teveel om op alle mogelijkheden in te gaan. In dit hoofdstuk leer je de basis aan de hand van het werken met lokaal opgeslagen tekstbestanden.

Bestanden openen

Er zijn verschillende acties die je kunt uitvoeren met een tekstbestand. Voordat je ermee kunt werken, dien je het eerst te openen. Vergelijk het met een Wordbestand: wil je dit lezen of er tekst aan toevoegen, dan open je het ook eerst. Waar je dit bij het Wordbestand doet door te dubbelklikken op de bestandsnaam, open je in Python een bestand met de ingebouwde functie open().

Een moeilijkheid bij het openen van bestanden is dat je de juiste codering (encoding) moet kiezen wil je het bestand correct weergeven. Om moeilijkheden te voorkomen, maak je nu eerst zelf een bestand aan.

f = open("voorbeeld.txt", mode="wt", encoding="utf-8")
f.close()

Je leert hierna wat dit precies doet, voor nu is het alleen belangrijk te weten dat het bestand voorbeeld.txt wordt opgeslagen naast het bestand vanuit waar je deze code uitvoert. Heb je in Thonny deze code in main.py in een mapje 'Programmeren met Python` staan, dan zal voorbeeld.txt dus ook in het mapje 'Programmeren met Python' staan.

Werk vanaf nu verder in een Python-bestand dat in de map staat waar je voorbeeld.txt hebt aangemaakt.

Naar tekstbestanden schrijven

Je hebt nu een leeg tekstbestand. Tijd om er tekst aan toe te voegen.

f = open("voorbeeld.txt", mode="wt", encoding="utf-8")
f.write("Dit is mijn eerste zin in een tekstbestand.\n")
f.write("Dit is mijn laatste zin.")
f.close()

Op regel 1 zie je weer dezelfde aanroep naar open(). Het eerste argument is de bestandsnaam. In dit geval is het alleen de naam, maar het kan ook een volledig pad zijn naar een bestand.

Het tweede argument, hier expliciet gemaakt, is de modus (mode). Open je een bestand, dan dien je op te geven wat je ermee wilt doen en wat voor bestand het is. De eerste letter geeft aan of je het wilt lezen (read), schrijven (write) of beide. In dit geval gebruiken we w, waarmee je een bestand zowel kunt lezen als schrijven. Bestaat het bestand nog niet, dan zal het worden aangemaakt. De tweede letter geeft aan of het een tekstbestand betreft of een binair bestand (waarover later meer).

a

Voeg tekst toe aan het einde van het bestand.

w

Voeg tekst toe vanaf begin van bestand. Bestaande tekst wordt eerst verwijderd.

r

Lees de tekst, vanaf het begin.

Met het laatste argument, encoding, geef je de codering van het bestand op. Je hebt het bestand aangemaakt in utf-8, dus open je het nu ook weer met die codering.

Op regels 2 en 3 van het bestand schrijf je nu twee regels naar het tekstbestand. Let op de \n, je dient zelf eventuele linebreaks toe te voegen.

Als je klaar bent met het werken met een bestand, moet je het altijd sluiten. Dit doe je met .close().

Voer deze code uit en open voorbeeld.txt, bijvoorbeeld met Kladblok in Windows om te controleren of de tekst inderdaad is toegevoegd.

Opdracht 1: Toevoegen

Met mode wt schrijf je naar een bestand. Als het nog niet bestaat, wordt het bestand aangemaakt. Bestaat het al wel, dan overschrijf je het, eventuele bestaande tekst wordt dus overschreven.

Open nu nogmaals het bestand voorbeeld.txt en voeg de tekst "Nieuwe laatste zin." toe zonder de al bestaande tekst te overschrijven.

Klik om het antwoord te tonen

Een uitwerking is:

f = open("voorbeeld.txt", mode="at", encoding="utf-8")
f.write("\nNieuwe laatste zin.")
f.close()

Let op dat je \n toevoegt voor je tekst om de tekst op een nieuwe regel te laten beginnen.

Tekstbestanden lezen

Nu je een bestand hebt met tekst, kun je het ook lezen. Je gebruikt weer open() om het bestand te openen, maar nu in read-only modus:

f = open("voorbeeld.txt", mode="rt", encoding="utf-8")
tekst = f.read()
print(tekst)
f.close()

Met .read() lees je het hele bestand in één keer in. In dit geval geen probleem, zo veel tekst is het niet.

Met read() kun je ook kiezen hoeveel tekens je wilt inlezen. Na het inlezen wordt de 'cursor' als het ware verplaatst na het laatst ingelezen teken. Roep je daarna nog een keer read() aan, dan lees je de rest van de tekst in.

f = open("voorbeeld.txt", mode="rt", encoding="utf-8")
tekst = f.read(43)  # De eerste zin bestaat uit 43 tekens
print(tekst)

tekst = f.read()  # Lees de rest van het bestand
print(tekst)

f.close()
Opdracht 2: Per 50

Schrijf code dat voorbeeld.txt opent en 2 keer .read(50) aanroept. Wat is het resultaat?

Klik om het antwoord te tonen

Een uitwerking is:

f = open("voorbeeld.txt", mode="rt", encoding="utf-8")
print(f.read(50))
print(f.read(50))
f.close()

De eerste aanroep naar .read(50) lees de eerste 50 tekens in:

"Dit is mijn eerste zin in een tekstbestand.
Dit is"

De tweede aanroep naar .read(50) leest de volgende 50 tekens in. Er zijn nog minder dan 50 tekens te lezen, maar dat maakt voor de aanroep niet uit:

" mijn laatste zin.
Nieuwe laatste zin."

Met read() kun je het bestand regel voor regel inlezen, maar daarvoor moet je wel weten hoeveel tekens elke regel bevat. Dat is niet heel handig. Gelukkig is er ook de methode .readline():

f = open("voorbeeld.txt", mode="rt", encoding="utf-8")
regel_1 = f.readline()  # Lees de eerste regel
print(regel_1)

regel_2 = f.readline()  # Lees de tweede regel
print(regel_2)

regel_3 = f.readline()  # Lees de derde regel
print(regel_3)

f.close()

De print()-functie toont de \n niet, maar voer je regel_1 in de shell in, dan zul je het wel zien.

Dit is al een stuk beter, maar het kan nog eenvoudiger. Een bestandobject is namelijk een iterator, en dus kun je de for-loop gebruiken.

f = open("voorbeeld.txt", mode="rt", encoding="utf-8")

for line in f:
    print(line)

f.close()

Op deze manier print je alle regels een voor een af.

Opdracht 3: Kapitalen

Een for-loop gebruik je als je met alle regels iets wilt doen. Lees voorbeeld.txt nogmaals in en zorg ervoor dat alle regels in kapitalen (hoofdletters) worden geprint. Gebruik hiervoor de upper() methode van str.

Klik om het antwoord te tonen

Een uitwerking is:

f = open("voorbeeld.txt", mode="rt", encoding="utf-8")
for line in f:
    print(line.upper())
f.close()

Contextmanagers

Tot nu toe heb je het bestand steeds handmatig gesloten met f.close(). Maar omdat elk bestand dat geopend wordt, ook weer gesloten moet worden is er een handigere manier. Met een contextmanager zorg je ervoor dat elk bestand dat is geopend met open(), ook altijd weer wordt gesloten met .close(). Dat ziet er zo uit:

with open("voorbeeld.txt", mode="rt", encoding="utf-8") as f:
    for line in f:
        print(line)

In plaats van f = open(…​) schrijf je nu with open(…​) as f:, alles daarna is het blok van de contextmanager. In dit geval print je alle regels van het bestand. Omdat je met with werkt, hoef je het bestand niet meer expliciet af te sluiten, dit doet de contextmanager voor je. Zo kun je het sluiten niet per ongeluk vergeten.

Werken met andere (binaire) bestanden

Met open() open je niet enkel tekstbestanden, maar ook binaire bestanden (zoals afbeeldingen). Bij het argument mode geef je dan b mee in plaats van t, een encoding is in dat geval niet nodig. In het geval van afbeeldingen zul je dit echter vaak met afzonderlijke packages doen. Zo is Pillow een veelgebruikte package om met afbeeldingen te werken.

Naast packages om met binaire bestanden te werken, zijn er ook packages om met andere tekstbestanden te werken. Een aantal hiervan zijn aanwezig in de standaard bibliotheek, bijvoorbeeld:

  • csv, om met CSV-bestanden te werken

  • configparser, om met .ini configuratiebestanden te werken

  • json, om met JSON-bestanden te werken

  • html, om met HTML-bestanden te werken

Dit zijn zeker niet de enige packages in de standaard bibliotheek, kijk op python.org voor meer!

Daarnaast zijn er ook nog packages van derde partijen, zoals de al eerder genoemde Pillow, om met (tekst)bestanden te werken. Een veelgebruikte package om met HTML te werken is bijvoorbeeld Beautiful Soup