Ruwe inhoud lezen
Maak read_blob(path): geef de volledige inhoud als bytes terug, inclusief nulbytes en ongeldige UTF8. Verander het bronbestand niet.
Een afbeelding, archief of ontvangen pakketje hoeft geen leesbare tekst te bevatten. Toch wil je het exact kunnen bewaren of kopiëren. Als je bij ieder bestand automatisch UTF8 probeert te lezen, kan je programma geldige binaire gegevens weigeren of onbedoeld veranderen.
Een bytes-waarde is een onveranderlijke reeks getallen van nul tot en met 255. Eén index levert een integer; een slice levert weer bytes. De weergave met een b voor het aanhalingsteken maakt het type herkenbaar, maar is niet de opgeslagen inhoud zelf.
Een nulbyte is een gewoon onderdeel van zo'n reeks. Hetzelfde geldt voor de byte 255, ook als die op zichzelf geen geldige UTF8-letter vormt. Bij binaire I/O bewaren we zulke waarden zonder ze eerst in tekst om te zetten.
from pathlib import Path
path = Path("pakket.bin")
path.write_bytes(bytes([0, 255, 10]))
data = path.read_bytes()
print(type(data).__name__, len(data))
print(data[1])
print(data.hex())
print(data[:2].hex())bytes 3 255 00ff0a 00ff
hex() toont iedere byte als twee hexadecimale cijfers. Dat is handig om exact te vergelijken zonder onleesbare tekens naar de console te sturen. De string 00ff0a in de uitvoer is dus een leesbare beschrijving van drie bytes, geen bestand van zes letters.
Open met rb om bytes te lezen en met wb om bytes te schrijven. Daarbij geef je geen encoding mee: die parameter hoort bij de vertaalslag tussen tekst en bytes. Een with-context sluit ook een binaire stream op de bekende manier.
Voor kleine bestanden zijn Path.read_bytes() en Path.write_bytes(data) korte vormen. Schrijven vervangt de bestaande inhoud en kan het bestand dus ook korter of leeg maken. De leeswaarde is bytes; de schrijfoperatie zelf levert een aantal geschreven bytes, terwijl onze hulpfunctie volgens afspraak None teruggeeft.
stream.read(size) leest hoogstens zoveel bytes. Als het bestand kleiner is, krijg je de beschikbare inhoud; bij nul krijg je lege bytes. Dat aantal is een grens in bytes, niet in letters.
from pathlib import Path
path = Path("naam.bin")
path.write_bytes("café".encode("utf-8"))
data = path.read_bytes()
print(len(data), len(data.decode("utf-8")))
try:
data[:4].decode("utf-8")
except UnicodeDecodeError:
print("vier bytes vormen nog geen hele UTF8-tekst")
print(data.decode("utf-8"))5 4 vier bytes vormen nog geen hele UTF8-tekst café
De é gebruikt in UTF8 meer dan één byte. Een willekeurige byteprefix kan daardoor midden in die letter stoppen. Pas decode("utf-8") toe wanneer de gegevens volgens hun formaat complete UTF8-tekst zijn; niet om een willekeurig binair bestand makkelijker te kopiëren.
Voor de kleine oefenbestanden kun je bronbytes lezen en ze ongewijzigd naar een ander pad schrijven. De bron blijft daarbij intact en een bestaand doel wordt vervangen. Bij grote bestanden zou je opeenvolgende blokken lezen om niet alles tegelijk in geheugen te houden; dat bouwen we hier nog niet.
Met read_text(errors="ignore") lijkt een lastige afbeelding opeens leesbaar, maar ongeldige tekstbytes worden dan weggegooid. Terugschrijven maakt dat verlies niet ongedaan. Kies de juiste representatie voordat je leest, in plaats van decoderingsfouten te verbergen.
De prefixopdracht beoordeelt de teruggegeven bytes, niet hoeveel bytes een implementatie vooraf heeft ingelezen. Alles lezen en daarna slicen is hier dus een geldige vorm; bij grote bestanden vraagt geheugenverbruik een aparte afspraak.
Maak read_blob(path): geef de volledige inhoud als bytes terug, inclusief nulbytes en ongeldige UTF8. Verander het bronbestand niet.
Maak write_blob(path, data): vervang de inhoud door exact de meegegeven bytes, ook bij lege data. Geef None terug.
Maak read_prefix(path, size): geef hoogstens de eerste size bytes terug. size is niet-negatief; nul levert lege bytes en een te groot aantal levert de hele inhoud. Laat de bron intact.
Maak copy_blob(source, destination): kopieer de kleine volledige bron als bytes naar het doel. Vervang bestaande doelinhoud, behoud de bron en geef None terug.
De invoer is telkens bytes of een niet-negatieve maximale leesgrootte. De controle gebruikt onder meer nulbytes, ongeldige UTF8 en lege bestanden. Bewaar de bytevolgorde exact; voeg geen regeleinden of tekstcodering toe.
In de laatste les vervangen we een tekstbestand pas nadat de nieuwe versie klaarstaat.
Voer je code uit en ontdek
wat je hebt gemaakt.
Je hoeft het niet in één keer goed te doen. Na het uitvoeren zie je hier welke stappen al kloppen.