Beetje Coderen

Les 3

Strings en tekstverwerking in de diepte

Unicode, f-string-opmaak en stringmethodes voor echte teksten.

⏱ 45–60 minuten leestijd + oefenen

🎯 Na deze les kun je

  • f-strings met opmaakcodes gebruiken
  • Unicode en encodings uitleggen
  • tekst opschonen en splitsen

Een groot deel van wat programma's doen, is tekst verwerken: namen netjes maken, een logregel uit elkaar halen, een rapport uitlijnen, een CSV-bestand van een ander systeem inlezen dat net een andere tekenset gebruikt. Je kent strings al uit de cursus Programmeerconcepten. In deze les leer je de gereedschappen die Python daarvoor heeft: opmaakcodes in f-strings, de belangrijkste string-methodes en hoe tekst als bytes wordt opgeslagen (encodings).

F-strings en formaatcodes

Een f-string vult waarden in tussen accolades. Na een dubbele punt kun je zeggen hoe de waarde getoond moet worden:

prijs = 1234.5
print(f"{prijs:.2f}")      # 2 decimalen
print(f"{prijs:,.2f}")     # met duizendtalscheiding
print(f"{0.0712:.1%}")     # als percentage
print(f"{42:05d}")         # aanvullen met nullen tot 5 tekens
1234.50
1,234.50
7.1%
00042

Uitlijnen werkt met < (links), > (rechts) en ^ (midden) plus een breedte. Handig voor tabellen:

producten = [("appel", 0.5), ("watermeloen", 3.25), ("kiwi", 0.3)]
for naam, prijs in producten:
    print(f"{naam:<12}{prijs:>7.2f}")
print(f"[{'totaal':^19}]")
appel          0.50
watermeloen    3.25
kiwi           0.30
[      totaal       ]

Bij het debuggen is = handig: het toont de expressie én de waarde.

aantal = 3
prijs = 0.5
print(f"{aantal=} {aantal * prijs=}")
aantal=3 aantal * prijs=1.5

De duizendtalscheiding is altijd een komma en het decimaalteken een punt, ongeacht je taalinstelling. Voor de Nederlandse notatie (1.234,50) is er een eenvoudige truc: f"{prijs:,.2f}".replace(",", "X").replace(".", ",").replace("X", "."). Voor echte meertalige programma's gebruik je de module locale of de bibliotheek babel.

String-methodes

Strings zijn onveranderlijk: een methode verandert de string niet, maar geeft een nieuwe terug. Je moet het resultaat dus opslaan.

naam = "  Sam de Vries  "
naam.strip()
print(f"[{naam}]")
naam = naam.strip()
print(f"[{naam}]")
[  Sam de Vries  ]
[Sam de Vries]

De methodes die je het vaakst nodig hebt:

s = "Hallo, Wereld"
print(s.lower(), s.upper(), s.title())
print(s.startswith("Hal"), s.endswith("!"))
print(s.find("Wereld"), s.find("Mars"))
print(s.replace("Wereld", "Python"))
print(s.count("l"))
hallo, wereld HALLO, WERELD Hallo, Wereld
True False
7 -1
Hallo, Python
3

find geeft de positie, of -1 als het niet gevonden wordt. Wil je alleen weten óf iets erin staat, gebruik dan in: "Wereld" in s.

Splitsen en samenvoegen

split en join zijn elkaars tegenovergestelde:

regel = "2026-10-13;Utrecht;14.5"
datum, stad, temp = regel.split(";")
print(stad, float(temp) + 1)

woorden = ["een", "beetje", "coderen"]
print(" ".join(woorden))
print("-".join(woorden))
Utrecht 15.5
een beetje coderen
een-beetje-coderen

split() zonder argument splitst op elke reeks witruimte en negeert witruimte aan het begin en eind:

print("  twee   spaties  ".split())
print("  twee   spaties  ".split(" "))
['twee', 'spaties']
['', '', 'twee', '', '', 'spaties', '', '']

Let op: join is een methode van het scheidingsteken, niet van de lijst. En alle elementen moeten strings zijn: ", ".join([1, 2]) geeft een TypeError. Gebruik dan ", ".join(str(x) for x in getallen).

Uitgewerkt voorbeeld: namen opschonen

Uit een formulier komt een lijst namen, slordig ingevuld. Voorspel de uitvoer voordat je verder leest.

invoer = ["  sam DE vries", "NOOR jansen  ", "ali", ""]

namen = []
for ruw in invoer:
    schoon = " ".join(ruw.split()).title()
    if schoon:
        namen.append(schoon)

for nr, naam in enumerate(namen, start=1):
    print(f"{nr:>2}. {naam:<15}|")
 1. Sam De Vries   |
 2. Noor Jansen    |
 3. Ali            |
  • " ".join(ruw.split()) haalt alle dubbele spaties en de spaties aan de randen weg.
  • title() maakt van elk woord een hoofdletter, ook van "de". Dat is in Nederlandse namen niet altijd goed; netjes oplossen vraagt meer werk.
  • De lege string is "falsy" (if schoon is onwaar) en wordt overgeslagen.

Encodings: tekst als bytes

Een computer slaat geen letters op, maar bytes. Een encoding bepaalt hoe tekens in bytes worden omgezet. De standaard is tegenwoordig UTF-8: gewone Engelse letters kosten één byte, andere tekens twee tot vier.

tekst = "café €5"
data = tekst.encode("utf-8")
print(data)
print(len(tekst), len(data))
print(data.decode("utf-8"))
b'caf\xc3\xa9 \xe2\x82\xac5'
7 10
café €5

é is twee bytes, € drie. Een str bevat tekens, een bytes-object (herkenbaar aan de b ervoor) bevat bytes. Van tekst naar bytes is encode, terug is decode.

Gaat het mis, dan krijg je onleesbare tekens of een fout:

data = "café".encode("utf-8")
print(data.decode("latin-1"))
print(data.decode("ascii"))
café
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128)

café herken je vaak in de praktijk: UTF-8-bytes die als Latin-1 gelezen zijn. Geef bij het openen van bestanden daarom altijd de encoding op (meer in les 4):

with open("namen.txt", encoding="utf-8") as f:
    ...

Zonder encoding gebruikt Python de standaard van je besturingssysteem, en die is op Windows soms nog niet UTF-8. Hetzelfde programma kan dan op de ene computer werken en op de andere niet.

Veelgemaakte fouten

  • Het resultaat van een methode niet opslaan: naam.strip() in plaats van naam = naam.strip().
  • join aanroepen op de lijst: woorden.join(" ") geeft AttributeError: 'list' object has no attribute 'join'.
  • Getallen in een string optellen: "3" + "4" is "34". Zet eerst om met int() of float().
  • Bestanden openen zonder encoding="utf-8".

Oefenen

Oefening 1. Print voor de bedragen [3.5, 120, 0.99] een kolom met een euroteken, rechts uitgelijnd op 8 tekens met 2 decimalen, zoals € 3.50.

Modelantwoord oefening 1
for bedrag in [3.5, 120, 0.99]:
    print(f"€{bedrag:>8.2f}")
€    3.50
€  120.00
€    0.99

Oefening 2. Schrijf een functie initialen(naam) die van "sam de vries" de string "S.D.V." maakt.

Modelantwoord oefening 2
def initialen(naam):
    return "".join(woord[0].upper() + "." for woord in naam.split())

print(initialen("sam de vries"))
print(initialen("  Noor   Jansen "))
S.D.V.
N.J.

Oefening 3. Een bestand bevat de bytes b'Gr\xc3\xbcezi'. Hoe maak je er tekst van, en wat komt eruit?

Modelantwoord oefening 3
print(b'Gr\xc3\xbcezi'.decode("utf-8"))
Grüezi

\xc3\xbc is de UTF-8-codering van ü.

Lab: een rapport netjes uitlijnen

  1. In rapport.py staat een lijst met verkoopcijfers per medewerker (naam, aantal, omzet), slordig ingevoerd: namen met extra spaties en verkeerde hoofdletters.
  2. Schrijf de functie maak_rapport(regels) die een string teruggeeft met een tabel: de naam links uitgelijnd op 15 tekens, het aantal rechts op 5, de omzet rechts op 10 met 2 decimalen.
  3. Voeg onderaan een regel Totaal toe met de som van het aantal en de omzet.
  4. Lees ook namen_latin1.txt in: dat bestand is opgeslagen als Latin-1. Zorg dat Müller en Ørsted goed worden weergegeven.
  5. Controleer met beetje check python-les3.

Samenvatting

  • Formaatcodes in f-strings: :.2f, :,, :.1%, :05d, uitlijnen met <, > en ^ plus een breedte; {x=} voor debuggen.
  • Strings zijn onveranderlijk: sla het resultaat van een methode op.
  • strip, lower/upper/title, replace, find, startswith, split en join zijn je dagelijkse gereedschap.
  • Tekst (str) wordt bytes (bytes) via een encoding; gebruik UTF-8 en geef encoding="utf-8" altijd op bij bestanden.

In les 4 werk je met bestanden en paden met pathlib.

Lab

Een rommelige namenlijst opschonen

💻 Dit lab doe je op je eigen computer met Docker. Start de lab-container met (geeft de opdracht een eigen startcommando, gebruik dan dat):

$ docker run -it --rm registry.wizg.it/webhosting/beetje-labs:python-les3

Volg daarna de stappen van de opdracht hierboven. Nog geen Docker? Zie les 14 van Een beetje Linux & Docker. Voor dit lab is er geen live lab in de browser.

Klaar? Controleer je werk met:

$ beetje check python-les3

📝 Quiz: strings en tekstverwerking

6 vragen · 70% nodig om te slagen

1. Wat print `f"{3.14159:.2f}"`?
2. Na `naam = " Sam "` en `naam.strip()`, wat is de waarde van naam?
3. Hoe maak je van de lijst ["a", "b", "c"] de string "a-b-c"?
4. Wat geeft `"Hallo".find("x")`?
5. Je ziet `café` in plaats van `café`. Wat is er waarschijnlijk gebeurd?
6. Hoeveel bytes is de string "€" in UTF-8?