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
  • UNICODE geeft elk teken een nummer: € is 8364, op elke computer
  • Hoe dat nummer als bytes bewaard wordt, is een aparte afspraak: de encoding (UTF-8, UTF-16, …)

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 in het geheugen als UTF-16

Opmerking

UTF staat voor UNICODE Transformation Format, het getal is het aantal bits per blokje. In het geheugen gebruikt .NET UTF-16, naar het scherm gaat tekst vaak als UTF-8. Dat verschil komt terug bij het euroteken.

'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.

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

Een aanhalingsteken schrijf je in een verbatim string als "", niet als \": @"Hij zei ""hallo"" in C:\Temp"

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.

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

:C rekent niets om

double prijs = 12.34;   // in euro
Console.WriteLine($"{prijs:C}");

BE € 12,34

US $12.34

GB £12.34

JP ¥12: de centen zijn weg

Vastzetten, bovenaan je Main:

System.Globalization.CultureInfo.CurrentCulture =
    new System.Globalization.CultureInfo("nl-BE");

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.

Wat komt er door?

Console.WriteLine("A é € ╔ 😀");
A é ? ╔ ??
Console.OutputEncoding = System.Text.Encoding.UTF8;
Console.WriteLine("A é € ╔ 😀");
A é € ╔ 😀

Van code tot scherm

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

Alle kennisclips van H3: 10 korte clips over ASCII en Unicode, format specifiers, + op strings, escape characters, raw strings en Environment

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

Alles uit dit hoofdstuk op één A4.

Download de poster als pdf

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.

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.