De afgesproken CSV-kop en iedere gegevensrecord valideren.
Logische records van fysieke regels onderscheiden.
Een invoergrens en de levensduur van het bronbestand bewaken.
De parser kan een losse rij controleren. Nu moet een bestand met zulke rijen in een verzameling bruikbare registraties veranderen. De CSV-lezer krijgt daarbij twee extra verantwoordelijkheden: het bronbestand beheren en vertellen bij welke record een invoerprobleem hoort.
Laat CSV zijn eigen structuur lezen
Gebruik csv.reader voor komma's, citaten en velden. Zelf iedere regel op een komma splitsen verliest de betekenis van een geciteerd veld. De bibliotheek levert per logische record een lijst met veldteksten.
Eén record kan twee fysieke regels beslaan
import csv
import io
text = 'name,minutes\n"hulp\nbalie",15\n'
for number, row in enumerate(csv.reader(io.StringIO(text, newline='')), start=1):
print(number, repr(row))
Verwachte uitvoer
1 ['name', 'minutes']
2 ['hulp\nbalie', '15']
Het voorbeeld toont een record waarin één veld een regeleinde bevat. Het record blijft één lijst met twee velden. Je ziet het verschil tussen het aantal regels in de tekst en het aantal records dat de lezer oplevert.
Voor ons urenbestand open je de bron met UTF8 en newline="". De CSV-bibliotheek verzorgt dan zelf de behandeling van regeleinden. Met strict=True vraag je om een fout bij ongeldige CSV-structuur, zoals een niet afgesloten citaat.
Eerst de kop, daarna de gegevens
De kop moet precies date,team,minutes zijn, in die volgorde. Dat voorkomt dat een bestand met verwisselde kolommen schijnbaar geldige maar verkeerd begrepen gegevens oplevert. Een ontbrekende of anders gespelde kop is daarom een InputError.
De kop telt niet als registratie
import csv
import io
rows = csv.reader(io.StringIO('team,minutes\nbouw,20\nbalie,15\n'))
print(next(rows))
for number, row in enumerate(rows, start=2):
print('record', number, row)
Verwachte uitvoer
['team', 'minutes']
record 2 ['bouw', '20']
record 3 ['balie', '15']
De kop is apart opgehaald voordat de genummerde lus begint. In het urenproject gaat iedere volgende record naar parse_record. Zo blijft kennis over kalenderdatums en minuutgetallen bij de parser uit de eerste les.
Een bestand met alleen de juiste kop levert een lege lijst. Een lege fysieke regel erna is daarentegen een ontvangen record zonder de drie vereiste velden. Die sla je niet stil over.
Context toevoegen zonder de oorzaak kwijt te raken
Een melding als onbekend team vertelt nog niet waar een gebruiker moet kijken. Nummer de gegevensrecords vanaf twee, met de kop als één. Voeg die context toe wanneer parse_record een InputError geeft.
De foutoorzaak blijft beschikbaar met raise ... from error. De nieuwe melding kan bijvoorbeeld record 3: onbekend team zijn, terwijl de oorspronkelijke parserfout als cause bewaard blijft. De aanroeper krijgt zo zowel bruikbare context als de oorspronkelijke reden.
Een csv.Error gaat over de structuur van het document zelf. Vertaal die naar de afgesproken InputError voor ongeldige CSV. Fouten bij openen of UTF8-decoding blijven hun oorspronkelijke fouttype houden voor de latere CLI-melding.
Een maximum is een grens met twee kanten
De projectaanroep accepteert maximaal duizend gegevensrecords. De parameter max_rows maakt de gekozen limiet ook met kleine proefbestanden controleerbaar. Controleer een positieve integer en sluit bool uit voordat je het bestand opent.
Precies de gekozen hoeveelheid mag nog passen. Zodra een volgende record verschijnt, meld je dat de limiet is overschreden. Controleer daarom het aantal reeds toegevoegde registraties vóór je een nieuwe toevoegt.
Herhaalde registraties horen ook bij de telling. Een verzameling unieke modellen zou hier ongemerkt gegevens verwijderen. De lijst bewaart iedere geldig ontvangen record, in de ontvangen volgorde.
Veelgemaakte fout: bij een probleem een lege lijst geven
Een lege lijst kan een geldig resultaat zijn van een bestand met alleen een kop. Dezelfde waarde na een leesfout verbergt echter dat het rapport onvolledig zou zijn. Laat een onopgelost probleem daarom doorgaan.
Het with-blok sluit de bron bij normale afronding én bij een fout tijdens het lezen. De regiefunctie hoeft dat bestand niet alsnog op te ruimen. Zij kan vertrouwen op de afgesproken verantwoordelijkheid van deze module.
AAN DE SLAG 0/4
1
De bron en de kop lezen
Maak read_sessions(path, *, max_rows=1000). Open de tekstbron als UTF8 met newline="" en gebruik csv.reader met strict=True. Vereis exact de kop date,team,minutes, anders InputError("verwacht de kop date,team,minutes"). Geef een lijst Session-objecten terug door iedere record via parse_record om te zetten; alleen een geldige kop geeft [].
2
Een fout krijgt de juiste recordcontext
Nummer gegevensrecords vanaf 2, de kop telt als 1. Laat lege of afwijkende records niet weg. Maak van een InputError uit parse_record een InputError met "record <nummer>: <oude melding>" en behoud de oorspronkelijke fout als cause. Een csv.Error wordt InputError("de CSV-structuur is ongeldig") met die CSV-fout als cause.
3
Een gekozen maximum bewaken
Controleer max_rows vóór je het bestand opent: een positieve int, met bool uitgesloten; anders InputError("de recordlimiet moet positief zijn"). Accepteer precies max_rows gegevensrecords. Een volgende record geeft InputError("maximaal <max_rows> registraties toegestaan"). De gewone projectaanroep gebruikt 1000.
4
De bron op ieder eindpad sluiten
Het geopende bestand is gesloten vóór terugkeer of foutdoorgifte. Laat oorspronkelijke I/O- en Unicodefouten ongewijzigd door; maak daarvan geen leeg succesrapport. Ook een invoerfout sluit de bron.
Samenvatting
De CSV-lezer beheert structuur, kop en bronbestand; de parser beheert de velden.
Recordcontext telt logische records en bewaart de oorspronkelijke foutoorzaak.
Precies de invoergrens past; fouten worden gesloten en zichtbaar afgehandeld.