Zie Scherp Scherper - Hoofdstuk 18
Na dit hoofdstuk kan je:
System.IO: paden, bestanden en foldersStreamReader, StreamWriter en de File-klasseusing-blok inzetten om bestanden netjes vrij te gevenBinaryWriter en BinaryReaderFileInfo en DirectoryInfoHiermee lees, schrijf, maak en verwijder je bestanden en folders. Maar: je raakt nu echt de harde schijf.
Belangrijk
Tijd voor een back-up, en voor exception handling: bestanden luisteren minder goed dan jij. Eén foutje wist zomaar gegevens (ja, ook mij is dat al overkomen).
Elk bestand heeft een uniek path, in C# steeds een string:
\, macOS gebruikt /Bouw paden platformonafhankelijk met de Path-klasse:
Tip
Path heeft ook GetTempPath, GetFileNameWithoutExtension, … En via Environment.GetFolderPath(Environment.SpecialFolder.Desktop) vind je het bureaublad van de gebruiker.
Controleer of iets bestaat vóór je het gebruikt, en overschrijf nooit zomaar:
Console gebruik je WriteLinetrue = toevoegen (append); false overschrijftWaarschuwing
Zo “bloot” gebruik je het beter niet: bij een fout blijft het bestand open en gelockt. Verderop lossen we dat op met using.
ReadLine geeft null zodra het einde van het bestand bereikt is: zo weet de loop wanneer te stoppen.
Een using-blok geeft een bestand gegarandeerd weer vrij, ook bij een fout:
Bij de sluitende accolade roept C# automatisch Dispose() aan: het bestand gaat netjes dicht.
Tip
Vanaf nu zetten we altijd een using rond een StreamWriter of StreamReader.
De File-klasse kan een bestand in één keer inlezen, zonder lus:
File.ReadAllText / ReadAllLines (kort en leesbaar)StreamReader met de while-lusstring path = "dagboek.txt";
Console.WriteLine("Dagboek:");
using (StreamReader reader = new StreamReader(path))
{
string regel;
while ((regel = reader.ReadLine()) != null)
Console.WriteLine(regel);
}
Console.WriteLine("Geef je volgende schrijfsel:");
string entry = Console.ReadLine();
using (StreamWriter writer = new StreamWriter(path, true))
{
writer.WriteLine($"\n{DateTime.Now}");
writer.WriteLine(entry);
}Met een BinaryWriter schrijf je elk datatype weg:
FileMode (Create, Append, Open, …) bepaalt hoe het bestand geopend wordt.
Belangrijk
De volgorde is cruciaal: lees in exact dezelfde volgorde als je schreef. Wissel je ReadInt32 en ReadBoolean om, dan crasht je programma met een EndOfStreamException.
Elk bestand is uiteindelijk gewoon een rij bytes. Toon ze als hexadecimaal:
Tip
{b:X2} toont elke byte als twee hexadecimale tekens. Zo zie je letterlijk wat een BinaryWriter op schijf zette.
Schrijf een waarde weg en kijk hoe groot het bestand wordt:
Tip
Probeer het met een bool, een int, een double, een char. Herken je de groottes uit hoofdstuk 2?
Een FileInfo-object geeft info over een bestand én bewerkingsmethoden:
CopyTo, MoveTo en Delete doen wat je verwacht.
Tip
File vs FileInfo? File is een static klasse (snel, kort iets doen). FileInfo instantieer je met new (handig voor meerdere bewerkingen na elkaar).
GetFiles en GetDirectories geven de inhoud terug*.txt, Tim?.*) filtert; SearchOption.AllDirectories zoekt ook in subfoldersMet System.IO.Compression pak je hele mappen in of uit:
Tip
Handig voor back-ups of om veel bestanden in een keer te versturen. Voeg using System.IO.Compression; toe.
Een bestand of API-antwoord ophalen doe je met HttpClient:
Opmerking
HttpClient is de moderne opvolger van het oudere WebClient. Het await hoort bij asynchroon programmeren (verdiepingsstof voor later).
Objecten met de hand wegschrijven (string-conversies, byte-volgorde) is lastig. Serialiseren doet het voor je: een object “in serie” naar een bestand en terug.
Zo maak je letterlijk een savepoint van je programma.
JSON combineert het beste van beide werelden: compact én leesbaar.
Gebruik System.Text.Json. Serialiseren werkt op de publieke kant van je object:
Tip
Met new JsonSerializerOptions { WriteIndented = true } krijg je nette inspringing. Een hele List<Student> serialiseert net zo makkelijk.
De omgekeerde weg is generic: je geeft het verwachte type mee.
Opmerking
Met attributen stuur je het gedrag bij: [JsonIgnore] slaat een property over, [JsonPropertyName("...")] hernoemt ze, [JsonInclude] neemt iets privaat toch mee.
Onbekende JSON van bv. een web-API? Laat VS de klassen voor je schrijven:
BOEM!
System.IO geeft toegang tot de harde schijf: werk voorzichtig en met exception handlingStreamReader/StreamWriter (in een using-blok) en de File-klasse lezen en schrijven tekstBinaryWriter/BinaryReader verwerken elk datatype, maar de volgorde teltFileInfo en DirectoryInfo geven info en bewerkingenTip
En dat was het laatste hoofdstuk. Je bent van “Hello World!” tot serialisatie geraakt: dik verdiend. Blijf vooral oefenen en bouwen.
Kennisclips
Dezelfde concepten uit dit hoofdstuk, maar dan in andere talen. Handig om later code in Python of JavaScript te leren lezen.
In Python doet with open exact wat ons using-blok doet: het bestand gaat automatisch dicht aan het einde van het blok, ook bij een fout.
Waar C# een StreamReader plus using nodig heeft, vangt Python beide in één korte regel.
JSON staat voor JavaScript Object Notation en komt dus letterlijk uit JavaScript. Geen aparte JsonSerializer nodig: serialiseren zit ingebouwd.
Nóg directer dan C#: omdat JSON uit JavaScript stamt, is een object en zijn JSON-voorstelling bijna hetzelfde ding.
Python gebruikt zijn ingebouwde json-module: json.dumps serialiseert, json.loads deserialiseert.
Verschil met C#: je hoeft geen type mee te geven. Python geeft een dict terug, waar C# via Deserialize<Student> weet bij welke klasse de data hoort.
Een handige samenvattende poster (Opgelet: gemaakt m.b.v. ChatGPT Image Gen 2).