Wat leer je in dit hoofdstuk?

Na dit hoofdstuk kan je:

het verschil tussen char en string uitleggen

ASCII en UNICODE situeren en weten hoe .NET tekst bewaart

\ escape characters (\n, \t, \", …) gebruiken en weten waarom een Enter uit twee tekens bestaat

@ tekst letterlijk laten staan met het verbatim teken @ of met een raw string literal (""")

tekst samenvoegen en formatteren met string interpolation ($)

speciale UNICODE-tekens en emoji op het scherm tonen

info over de machine ophalen via Environment en je programma afsluiten met een exitcode

Alles is een char

Elk teken dat je intypt is een char

Je toetsenbord toont maar een fractie van alle mogelijke tekens (vergelijk met een Spaans, Tunesisch of Chinees toetsenbord)

Voor we tekst inlezen, moeten we begrijpen hoe een computer al die tekens voorstelt.

ASCII: elk teken een getal

  • Een afspraak die bepaalt welk teken bij welke waarde hoort
  • Iedereen die ASCII volgt, kan berichten met die tekens naar elkaar sturen
  • Maar 128 tekens, want 7 bits
  • Oorspronkelijk gemaakt om via de Telex te communiceren

De ASCII-tabel

De eerste 128 tekens (bron: Wikipedia).

Tip

De eerste 32 tekens zijn “onzichtbare” stuurtekens: commando’s om oude typemachines aan te sturen (line feed, bell, form feed, …).

UNICODE: de opvolger van ASCII

  • Door de verdere digitalisering bleek ASCII al snel te klein
  • UNICODE heeft plaats voor meer dan 1 miljoen tekens: elke letter, smiley en pictogram, wereldwijd op dezelfde manier
  • De eerste 128 tekens zijn die van ASCII, dus beide zijn compatibel
  • .NET bewaart een string intern als UTF-16 (er bestaan ook UTF-8 en UTF-32)

Eén kastje, een hele muur

Char en string in C

Char

char eenLetter = 'X';
Console.WriteLine(eenLetter);
X

'X' Één enkel karakter, tussen apostrofs

Elke char is 16 bits groot: .NET bewaart tekst als UTF-16 (er bestaan ook UTF-8 en UTF-32)

Opmerking

UTF staat voor UNICODE Transformation Format: de manier waarop een UNICODE-waarde in bits wordt bewaard. Het getal is het aantal bits per blokje: UTF-16 werkt met blokjes van 16 bits.

'7' is geen 7

char eenGetal = '7';
Console.WriteLine(eenGetal + 1);
56
  • '7' is een teken, geen getal
  • In de char zit de UNICODE-waarde van dat teken: 55 (kijk maar in de ASCII-tabel)
  • Reken je ermee, dan reken je dus met 55, niet met 7

Belangrijk

Wil je met een cijfer rekenen, dan moet je het eerst converteren naar een getal. Dat zien we in hoofdstuk 4.

Wat zit er in het kluisje?

String

Een string is een reeks van 0, 1 of meer char-elementen.

'H' een char gebruikt een apostrof

"Hallo" een string: tekst staat tussen dubbele aanhalingstekens

De string "Hallo" bestaat uit vijf char-elementen na elkaar.

Tip

In hoofdstuk 8 ontdek je dat een string eigenlijk een array van chars is, en leer je de string-methodes (ToUpper, Trim, Replace, …).

Drie keer “1”, drie types

char eenKarakter = '1';
string eenString = "1";
int eenGetal = 1;

Console.WriteLine(eenKarakter);
Console.WriteLine(eenString);
Console.WriteLine(eenGetal);
1
1
1

Driemaal een 1 onder elkaar, maar telkens een ander type. Boeiend programma, hoor.

Excel en de genen

In 2020 kregen

27

menselijke genen een nieuwe naam. Door Excel.

SEPT1

de naam van een gen

1 september

Excel maakt er een datum van

SEPTIN1

de nieuwe naam

In C# kies jij het type: string gen = "SEPT1"; blijft tekst.

Escape characters

Wat zijn escape characters?

Niet de boeiendste materie , maar wie ze beheerst, tovert mooiere tekst op het scherm.

Sommige tekens hebben een speciale functie: * bv. " begrenst een string * bv. \' begrenst een char

Met een backslash (\) zeg je: behandel het volgende teken letterlijk. Dit heet een escape character.

Het probleem: hoe print je een aanhalingsteken?

char weglatingsteken = ''';  // VS raakt volledig in de war

Escape characters to the rescue:

char weglatingsteken = '\'';

De belangrijkste escape characters

\'      apostrof
\"      aanhalingsteken
\\      backslash
\n      nieuwe lijn (enter / newline)
\t      horizontale tab
\uxxxx  teken met hexadecimale UNICODE-waarde xxxx

Niet vanbuiten leren

in de praktijk grijp je vooral naar \n, \t, \" en \\. De rest zoek je op.

Opmerking

\a laat de computer biepen. In Windows Terminal en standaard ook op Linux en Mac hoor je niets: het teken wordt genegeerd. Je code is dus niet stuk.

Van de schrijfmachine

Escape in strings

string woord = "\'s avonds";

Om code op het scherm te tonen:

Console.WriteLine("Console.WriteLine(\"Cool he\");");
Console.WriteLine("Cool he");

Witregels en tabs

string eenString = "Een zin.\t na een tab \nDan een nieuwe regel";
Console.WriteLine(eenString);
Een zin.         na een tab
Dan een nieuwe regel

Tip

\t springt naar de volgende vaste tabstop, handig om data uitgelijnd in een tabel te zetten.

De entertoets: \n en \r

Duw je op Enter, dan gebeuren er historisch twee dingen. De termen komen van de typemachine:

\r\n of \n?

\r\n Windows schrijft een nieuwe regel als \r\n

\n Linux en macOS gebruiken enkel \n

Console.Write($"Eerste regel{Environment.NewLine}Tweede regel");
Eerste regel
Tweede regel

Tip

Environment.NewLine kiest zelf het juiste teken voor het systeem waarop je code draait. In je console volstaat \n; zodra je naar een bestand schrijft of over het netwerk verstuurt, begint het verschil te tellen.

Het verbatim teken @

Het apenstaartje @ voor een string zegt: behandel alles letterlijk

escape characters worden genegeerd

string zonderAt = "C:\\Temp\\Myfile.txt";
string metAt = @"C:\Temp\Myfile.txt";

Opmerking

Aanhalingstekens moet je nog steeds escapen. Heb je een " in je tekst, dan kan @ niet helpen.

Console.WriteLine(metAt);
Console.WriteLine(@"Regel 1\nRegel 2");
C:\Temp\Myfile.txt
Regel 1\nRegel 2

@ en $ samen

string naam = "Finkelstein";
Console.WriteLine($@"Je bestanden staan in C:\Users\{naam}\Documents");
Je bestanden staan in C:\Users\Finkelstein\Documents

Tip

Zonder @ compileert dit niet: \U en \D zijn geen geldige escape characters.

Strings samenvoegen

String interpolation met $

Zet een $ voor de string en alles tussen { } wordt als code uitgevoerd:

int leeftijd = 13;
string naam = "Finkelstein";
string zin = $"Ik ben {naam} en ik ben {leeftijd} jaar.";

Je kende dit al uit hoofdstuk 1: op de plek van {naam} komt de inhoud van de variabele.

Expressies in interpolatie

Tussen de accolades mag elke expressie:

string a = $"Ik ben {leeftijd + 4} jaar.";          // 17
string b = $"Je naam telt {naam.Length} tekens.";   // aantal tekens
Console.WriteLine($"3 maal 9 is {3 * 9}");           // 27

De expressie

{leeftijd + 4}

wordt eerst berekend

17

dan in de tekst geplaatst

Ik ben 17 jaar.

Formatteren

Plaats een dubbelpunt na de expressie om de weergave te sturen:

double number = 12.345;
Console.WriteLine($"{number:F2}");   // 12,35  (afgerond, niet afgekapt)

F2 kommagetal met 2 cijfers na de komma

D5 geheel getal met minstens 5 cijfers (123 wordt 00123)

E2 wetenschappelijke notatie (12000000 wordt 1,20E+007)

C geldbedrag volgens de landinstellingen

Formatteren met een masker

double number = 12.3;
Console.WriteLine($"{number:0.00}");    // 12,30
Console.WriteLine($"{number:000.00}");  // 012,30
12,30
012,30

Waarschuwing

Komma of punt hangt van de computer af. Op een Belgisch systeem zie je 12,30, op een Engels 12.30. Dat maakt je uitvoer machine-afhankelijk. Wil je dat vastzetten, dan leer je later CultureInfo kennen.

In je C# code schrijf je je komma’s altijd met een punt**!

De +-operator op strings

+ plakt strings aan elkaar (concatenatie):

string zin = "Ik ben " + naam + " en ik ben " + leeftijd + " jaar.";

Zelfde resultaat, maar je let zelf op de spaties en de zin valt in stukken

$ Voor zulke zinnen: interpolatie

Wanneer wél +?

Lange tekst over meerdere lijnen, met + op het lijneinde:

Console.WriteLine("Je wordt wakker in een donkere kamer. " +
                  $"Het enige licht komt van een scherm, {naam}. " +
                  "Typ START om te beginnen.");

Belangrijk

Elk stuk met accolades heeft zijn eigen $ nodig. Een $ op de eerste lijn geldt niet voor de rest.

Tekst stap voor stap opbouwen met tekst += stuk; (komt terug bij herhalingen, H6).

Opletten met de volgorde bij +

Bij + met strings en getallen door elkaar, van links naar rechts:

Console.WriteLine("1" + 1 + 1);   
Console.WriteLine(1 + 1 + "1");   
Console.WriteLine("1" + (1 + 1)); 
111
21
12

Zodra een string meedoet, is het resultaat een string.

Van links naar rechts, haakjes gaan voor.

Volgorde bij +, stap voor stap

De char-val: ‘A’ + ‘B’

char letter1 = 'A';
char letter2 = 'B';
Console.WriteLine(letter1 + letter2);
131

Op het scherm: 131, niet “AB”.

Een char bewaart een getal: A = 65, B = 66. De +-operator bestaat niet voor char, dus C# rekent met int: 65 + 66 = 131.

Terug naar de kluisjes

Bovenkast en onderkast

Waarom net 32?

Console.WriteLine('a' - 'A');

32

plaatsen tussen elke hoofdletter en zijn kleine letter.

A = 65

0100 0001

a = 97

0110 0001

Hoofdletter maken? Zet dat ene bitje uit.

Daarom koos ASCII in 1967 voor precies 32 plaatsen verschil.

Vreemde tekens in console tonen

Speciale UNICODE-tekens tonen

Zet als allereerste lijn in Main, nog voor je iets naar het scherm stuurt:

Console.OutputEncoding = System.Text.Encoding.UTF8;

Zet je die lijn later, dan kan tekst die je daarvoor al toonde verkeerd verschijnen.

Dit Wikipedia-logo bestaat volledig uit UNICODE-tekens.

Tip

Ook het euroteken bij {bedrag:C} heeft die lijn nodig. Een consolevenster op Windows werkt standaard met 256 tekens en daar zit de € niet in.

Wat is 0x?

UNICODE-tabellen geven hun codes hexadecimaal: tellen met zestien cijfers, 0 tot 9 en dan A tot F. In C# zet je er 0x voor.

'A' = 65

0x41

4 × 16 + 1 = 65

'©' = 169

0xA9

A (= 10) × 16 + 9 = 169

Hetzelfde getal in het kluisje, enkel anders opgeschreven.

Drie manieren, één teken

Zoek de hexadecimale waarde op in een UNICODE-tabel (symbl.cc), voor © is dat 00A9:

Console.OutputEncoding = System.Text.Encoding.UTF8;

Console.WriteLine("©");         // 1. het teken zelf plakken
char copyright = (char)0x00A9;  // 2. de code casten naar char
Console.WriteLine(copyright);
Console.WriteLine("\u00A9");    // 3. de \u-notatie in een string
©
©
©

En emoji?

Een char gaat maar tot 0xFFFF, en \u verwacht exact vier cijfers. Emoji zitten daarboven. Oplossing? \U i.p.v. \u, en acht cijfers. Of plak de emoji gewoon in je string:

Console.OutputEncoding = System.Text.Encoding.UTF8;

char blij = (char)0x1F600;        // compileert niet
Console.WriteLine("\U0001F600");  // happy face!
Console.WriteLine("😀");          // ook goed: gewoon plakken

Opmerking

.NET bewaart zo’n teken als twee 16-bit stukjes (een surrogate pair). Eén emoji telt daardoor als 2 in string.Length. Die .Length is dus niet altijd het aantal zichtbare tekens.

Mojibake

Ooit een mail gekregen met

Café

in plaats van Café?

En een euroteken wordt zo €.

UNICODE-kunst

Multiline ASCII-art toon je met een verbatim string @:

string titel = @"
 _______ _____
|__   __|  __ \
   | |  | |  | |
   | |  | |  | |
   |_|  |_|__/ ";
Console.WriteLine(titel);

Tip

$ en @ mogen samen: zowel $@"..." als @$"..." werkt en doet hetzelfde.

Raw string literals

Nadeel van @: ook de spaties waarmee je je code inspringt horen bij de string. Sinds C# 11 kan het netter, met drie aanhalingstekens:

string titel = """
     _______ _____
    |__   __|  __ \
       | |  | |  | |
       |_|  |_|__/
    """;

De sluitende """ bepaalt welke inspringing weggeknipt wordt

\ Backslashes en \t blijven letterlijk, net als bij @

$ Een $ ervoor mag ook: $"""..."""

Opmerking

@"..." blijft gewoon werken. Je ziet het nog volop in bestaande code en in wat AI-tools genereren.

Environment bibliotheek

Wat vertelt Environment?

Console is maar één bibliotheek. Environment vertelt iets over de machine waarop je programma nú draait:

string gebruiker = Environment.UserName;
string pcnaam = Environment.MachineName;
int aantalProcessoren = Environment.ProcessorCount;

Console.WriteLine($"Hallo {gebruiker}, welkom op {pcnaam}.");
Console.WriteLine($"Hallo {Environment.UserName}!");   

Belangrijk

Hetzelfde programma geeft op elke machine een andere uitvoer. Test zulke code op meer dan één systeem.

Zelf op ontdekking

Typ in Visual Studio Environment. en kijk wat IntelliSense voorstelt: OSVersion, CurrentDirectory, … Niemand kent élke bibliotheek: het is aan jou om te ontdekken welke nuttig zijn voor jouw probleem.

long geheugen = Environment.WorkingSet;
Console.WriteLine($"Dit programma gebruikt {geheugen / 1024 / 1024} MB geheugen");

WorkingSet is het geheugen dat Windows aan je programma toewees. Dat getal verschilt per pc, en zelfs bij elke run.

Programma afsluiten

Environment.Exit(1);

Stopt je programma onmiddellijk. Alles wat erna staat, wordt niet meer uitgevoerd.

Het getal is de exitcode:

0 betekent “alles goed”

elk ander getal betekent “er ging iets mis”

Je zag ze al staan in Visual Studio: exited with code 0

Conclusie

Een char is één teken (een 16-bit getal), een string is een reeks chars

UNICODE (UTF-16 in .NET) volgt ASCII op en bevat meer dan een miljoen tekens

\ Escape characters (\n, \t, \") sturen hoe tekst geïnterpreteerd wordt; @ en """ schakelen ze uit

String interpolation ($"...{expressie}...") is de standaardmanier om variabelen in tekst te plaatsen; + vooral om lange tekst over meerdere lijnen te splitsen

Let op de char-val ('A' + 'B' = 131) en op machine-afhankelijke formattering

Environment geeft info over de machine (UserName, MachineName, NewLine) en Exit(0) stopt je programma met een exitcode

Tip

Volgende halte: werken met data: converteren, casten en rekenen.

Meer weten

Kennisclips

Oefeningen H3 · Quizlet flashcards

Zie verder

Hoe doen andere programmeertalen hetzelfde? En een korte zoek-de-fout, om zo kritisch mogelijk naar code te leren kijken.

Zie verder: zoek de fout

Deze regel wil tonen waar een bestand bewaard werd, maar de uitvoer ziet er raar uit:

Console.WriteLine("Bestand bewaard in C:\temp\notities.txt");

\t is een tab en \n een nieuwe lijn:

Bestand bewaard in C:   emp
otities.txt

Zet er een @ voor: @"C:\temp\notities.txt". Of verdubbel elke backslash: "C:\\temp\\notities.txt".

Zie verder: string als char-array

In C bestaat er geen echt string-type. Tekst is daar letterlijk een array van char, afgesloten met een null-karakter \0:

char woord[] = "Tim";   // {'T', 'i', 'm', '\0'}
printf("%s\n", woord);
Tim

In C# is string een volwaardig type met handige methodes. Het idee “een string is een reeks chars” zie je in C dus heel letterlijk terug.

Zie verder: het verbatim-idee elders

Python kent net hetzelfde, daar heet het een raw string. Je plaatst een r voor de string in plaats van een @:

met_r = r"C:\Temp\Myfile.txt"

Backslashes worden gewoon als tekst behandeld, niet als escape character. Handig voor bestandspaden en reguliere expressies.

Zie verder: interpolatie elders

Python (f-string) en JavaScript (template literal) doen exact hetzelfde, met een ander tekentje vooraan:

zin = f"Ik ben {naam} en ik ben {leeftijd} jaar."
const zin = `Ik ben ${naam} en ik ben ${leeftijd} jaar.`;

Samenvattende poster

Een handige samenvattende poster (Opgelet: gemaakt m.b.v. ChatGPT Image Gen 2).

Samen bouwen: een kerker

Het plan

Het startscherm van een spel zoals Dwarf Fortress, en op het einde een voorsmaakje van H6.

Muren

één string

Inrichten

jullie kiezen

Kleur

jij in het geel

Status

naam, levens, goud

Breek het

wat gebeurt er?

Animeren

elke Enter meer goud

Stap 1: de muren

Hoe zet je deze kamer met zo weinig mogelijk code op het scherm?

╔════════════════════╗
║                    ║
║                    ║
║                    ║
║                    ║
╚════════════════════╝

De muren staan niet op je toetsenbord: kopieer ze van deze slide

Zes lijnen in één keer: een raw string met """

Zoals altijd bovenaan: de UTF-8-lijn

Stap 2: wat staat er in de kamer?

Jullie beslissen. Een paar klassiekers:

@ jij, de held

☺ een dwerg, zoals in Dwarf Fortress

$ goud

g een goblin

~ water

+ een deur

Belangrijk

Tel je hokjes: elke lijn blijft even lang, anders staat de rechtermuur scheef.

Stap 3: jij in het geel

Hoe maak je enkel de @ geel?

╔════════════════════╗
║ $                  ║
║        @           ║
║                g   ║
║   ~~~~             ║
╚═════════+══════════╝

Eén string heeft één kleur: knip de kamer in boven, de lijn met de @ en onder

Die ene lijn in drie stukken met Write, en ertussen de kleur wisselen

Op het einde ResetColor()

Stap 4: de statusregel

Onder de kamer: wie speelt er, hoeveel levens, hoeveel goud?

╔════════════════════╗
║ $                  ║
║        @           ║
║                g   ║
║   ~~~~             ║
╚═════════+══════════╝
tim     ♥♥♥     Goud: 0042

De naam van de speler: Environment.UserName

De levens: gewoon ♥♥♥ in de string

Goud altijd met vier cijfers: {goud:D4}

Tussen de stukken een \t

Stap 5: breek het

Drie keer iets veranderen. Wat gebeurt er?

Zet een draak 🐉 in de plaats van de goblin

De rechtermuur schuift op: een emoji is twee hokjes breed.

Zet de UTF-8-lijn in commentaar

De draak wordt ??. De muren blijven staan: die zitten in code page 850.

Zet je naam in de bovenmuur, met $"""

Past op jouw pc. Bij een langere gebruikersnaam staat de muur scheef.

Stap 6: animeren

Bij elke Enter: scherm leeg, alles opnieuw tekenen, en één goudstuk meer.

╔════════════════════╗
║ $                  ║
║        @           ║
║                g   ║
║   ~~~~             ║
╚═════════+══════════╝
tim     ♥♥♥     Goud: 0042
╔════════════════════╗
║ $                  ║
║        @           ║
║                g   ║
║   ~~~~             ║
╚═════════+══════════╝
tim     ♥♥♥     Goud: 0043
╔════════════════════╗
║ $                  ║
║        @           ║
║                g   ║
║   ~~~~             ║
╚═════════+══════════╝
tim     ♥♥♥     Goud: 0044

Stoppen: Ctrl + C

while (true) herhaalt alles tussen de accolades, voor altijd (meer daarover in H6)

Bovenaan in de lus: Console.Clear()

Onderaan: Console.ReadLine() wacht op Enter, daarna goud++

Waar zet je int goud = 42;?

Boven de lus. Erin begint goud elke ronde opnieuw bij 42.