Skip to content

4. Foutafhandeling

Bij het programmeren geldt een absolute regel: een programma mag nooit zomaar „crashen“. Alle fouten die tijdens de uitvoering van het programma kunnen optreden, moeten worden afgehandeld en er moeten duidelijke foutmeldingen worden gegenereerd.

Als we het eerder besproken voorbeeld van de belastingen nog eens bekijken: wat gebeurt er als de gebruiker willekeurig iets invoert voor het aantal kinderen? Laten we eens naar dit voorbeeld kijken:

1
2
3
C:\>cscript impots1.vbs o xyzt 200000

C:\impots1.vbs(33, 3) Erreur d'exécution Microsoft VBScript: Type incompatible: 'cint'

Dit noemen we een onvoorspelbare crash. Er is een "crash" opgetreden bij de instructie kinderen=cint(wscript.arguments(1)) omdat arguments(1) de tekenreeks "xyzt" bevatte.

Voordat je een variant gebruikt waarvan je de exacte aard niet kent, moet je het exacte subtype ervan controleren. Dit kun je op verschillende manieren doen:

  • het werkelijke type van de gegevens in een variant testen met de functies vartype of typename
  • een reguliere expressie gebruiken om te controleren of de inhoud van de variant overeenkomt met een bepaald patroon
  • de fout laten optreden, deze vervolgens opvangen en vervolgens afhandelen

We bekijken deze verschillende methoden.

4.1. Het exacte type van een gegeven achterhalen

Laten we niet vergeten dat de functies vartype of varname het mogelijk maken om het exacte type van een gegeven te achterhalen. Dit helpt ons echter niet altijd evenveel. Wanneer we bijvoorbeeld gegevens lezen die via het toetsenbord zijn ingevoerd, zullen de functies vartype en typename aangeven dat het om een tekenreeks gaat, omdat alle via het toetsenbord ingevoerde gegevens als zodanig worden beschouwd. Dit vertelt ons echter niet of deze tekenreeks bijvoorbeeld als een geldig getal kan worden beschouwd. We gebruiken dan andere functies om toegang te krijgen tot dit soort informatie:

isNumeric(uitdrukking)
geeft ‘waar’ terug als expression als getal kan worden gebruikt
isDate(uitdrukking)
geeft ‘waar’ terug als uitdrukking als datum kan worden gebruikt
isEmpty(var)
is waar als de variabele var niet is geïnitialiseerd
isNull(var)
geeft 'waar' terug als de variabele var ongeldige gegevens bevat
isArray(var)
is waar als var een array is
isObject(var)
geeft 'waar' terug als var een object is

Het volgende voorbeeld vraagt om gegevens via het toetsenbord in te voeren totdat deze als een getal worden herkend:

Programma
' gegevens inlezen totdat deze als een getal worden herkend

Option Explicit

Dim fini, nombre

' er wordt een lus doorlopen zolang de ingevoerde gegevens niet correct zijn
' de lus wordt bestuurd door een booleaanse variabele die in het begin op 'false' staat (= nog niet voltooid)

fini=false
Do While Not fini
    ' er wordt om het getal gevraagd
    wscript.stdout.write "Tapez un nombre : "
    ' het wordt ingelezen
    nombre=wscript.stdin.readLine
    ' het type is bij het inlezen noodzakelijkerwijs een tekenreeks
    wscript.echo "Type de la donnée lue : " & typename(nombre) & "," & vartype(nombre)
    ' het werkelijke type van de ingelezen gegevens wordt gecontroleerd
    If isNumeric(nombre) Then 
        fini=true
    Else
        wscript.echo "Erreur, vous n'avez pas tapé un nombre. Recommencez svp..."
    End If
Loop

' bevestiging
wscript.echo "Merci pour le nombre " & nombre

' en einde
wscript.quit 0
Resultaten
1
2
3
4
5
6
Tapez un nombre : a
Type de la donnée lue : String,8
Erreur, vous n'avez pas tapé un nombre. Recommencez svp...
Tapez un nombre : -12
Type de la donnée lue : String,8
Merci pour le nombre -12

De functie isNumeric geeft niet aan of een uitdrukking een geheel getal is of niet. Om deze informatie te verkrijgen, moeten er aanvullende tests worden uitgevoerd. Het volgende voorbeeld vraagt om een geheel getal >0:

Programma
' gegevens worden ingelezen totdat deze worden herkend als een geheel getal >0

Option Explicit

Dim fini, nombre

' er wordt een lus doorlopen zolang de ingevoerde gegevens niet correct zijn
' de lus wordt bestuurd door een booleaanse variabele die in het begin op 'false' staat (= nog niet voltooid)

fini=false
Do While Not fini
    ' het getal wordt opgevraagd
    wscript.stdout.write "Tapez un nombre entier >0: "
    ' het wordt ingelezen
    nombre=wscript.stdin.readLine
    ' het reële type van de gelezen gegevens wordt gecontroleerd
    If isNumeric(nombre) Then
        ' is het een positief geheel getal (een getal dat gelijk is aan het gehele deel ervan)?
        If (nombre-int(nombre))=0 And nombre>0 Then
            fini=true
        End If
    End If
    ' eventuele foutmelding
    If Not fini Then wscript.echo "Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp..."
Loop

' bevestiging
wscript.echo "Merci pour le nombre entier >0 : " & nombre

' en einde
wscript.quit 0
Resultaten
1
2
3
4
5
6
7
8
Tapez un nombre entier >0: a
Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp...
Tapez un nombre entier >0: -1
Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp...
Tapez un nombre entier >0: 10.6
Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp...
Tapez un nombre entier >0: 12
Merci pour le nombre entier >0 : 12

Opmerkingen:

  • int(nombre) geeft het gehele deel van een getal weer. Een getal dat gelijk is aan zijn gehele deel is een geheel getal.
  • Het is interessant om op te merken dat de test If (getal - int(getal)) = 0 And getal > 0 moest worden gebruikt, omdat de test If getal = int(getal) And getal > 0 niet de verwachte resultaten opleverde. Deze test detecteerde geen positieve gehele getallen. We laten het aan de lezer over om uit te zoeken waarom.
  • De If-test (getal - int(getal)) = 0 is niet helemaal betrouwbaar. Laten we eens kijken naar het volgende uitvoervoorbeeld:
Tapez un nombre entier >0: 4,0000000000000000000000001

Merci pour le nombre entier >0 : 4,0000000000000000000000001

Reële getallen worden niet exact, maar bij benadering weergegeven. En hier leverde de bewerking nombre-int(nombre) 0 op, binnen de nauwkeurigheid van de computer.

4.2. Reguliere expressies

Met reguliere expressies kunnen we het formaat van een tekenreeks toetsen. Zo kunnen we controleren of een tekenreeks die een datum weergeeft, de indeling dd/mm/jj heeft. Hiervoor gebruiken we een patroon en vergelijken we de tekenreeks met dit patroon. In dit voorbeeld moeten j, m en a dus cijfers zijn. Het patroon voor een geldige datumnotatie is dan "\d\d/\d\d/\d\d", waarbij het symbool \d een cijfer aangeeft. De volgende symbolen kunnen in een patroon worden gebruikt (Microsoft-documentatie):

Teken
Beschrijving
\
Geeft aan dat het volgende teken een speciaal teken of een letterlijk teken is. Bijvoorbeeld: "n" staat voor het teken "n". "\n" staat voor een nieuwe regel. De reeks "\\" staat voor "\", terwijl "\(" staat voor "(".
^
Komt overeen met het begin van de invoer.
$
Komt overeen met het einde van de invoer.
*
Komt nul of meerdere keren overeen met het voorgaande teken. Zo komt "zo*" overeen met "z" of met "zoo".
+
Komt één of meerdere keren overeen met het voorgaande teken. Zo komt "zo+" overeen met "zoo", maar niet met "z".
?
Komt overeen met het voorgaande teken nul of één keer. Bijvoorbeeld: "a?ve?" komt overeen met "ve" in "lever".
.
Komt overeen met elk willekeurig teken, behalve het regeleinde-teken.
(sjabloon)
Zoekt naar modèle en slaat de overeenkomst op. De overeenkomende subreeks kan uit de verkregen verzameling Matches worden gehaald met behulp van Item [0]...[n]. Om overeenkomsten te vinden met tekens tussen haakjes ( ), gebruikt u "\(" of "\)".
x|y
Komt overeen met x of met y. Bijvoorbeeld: "z|foot" komt overeen met "z" of met "foot". "(z|f)oo" komt overeen met "zoo" of met "foo".
{n}
n is een niet-negatief geheel getal. Komt exact overeen met n maal het teken. Bijvoorbeeld: "o{2}" komt niet overeen met "o" in "Bob," maar met de eerste twee "o" in "fooooot".
{n,}
n is een niet-negatief geheel getal. Komt overeen met ten minste n keer het teken. Bijvoorbeeld: "o{2,}" komt niet overeen met "o" in "Bob", maar met alle "o" in "fooooot". "o{1,}" is gelijk aan "o+" en "o{0,}" is gelijk aan "o*".
{n,m}
m en n zijn niet-negatieve gehele getallen. Komt overeen met ten minste n en ten hoogste m keer het teken. Bijvoorbeeld: "o{1,3}" komt overeen met de eerste drie "o" in "foooooot" en "o{0,1}" komt overeen met "o?".
[xyz]
Tekenset. Komt overeen met een van de opgegeven tekens. Bijvoorbeeld: "[abc]" komt overeen met "a" in "plat".
[^xyz]
Negatieve tekenreeks. Komt overeen met elk teken dat niet is opgegeven. Bijvoorbeeld: "[^abc]" komt overeen met "p" in "plat".
[a-z]
Tekenreeks. Komt overeen met elk teken in de opgegeven reeks. Bijvoorbeeld: "[a-z]" komt overeen met elk klein letterteken tussen "a" en "z".
[^m-z]
Negatief tekenbereik. Komt overeen met elk teken dat niet in de opgegeven reeks voorkomt. Bijvoorbeeld: "[^m-z]" komt overeen met elk teken dat niet tussen "m" en "z" voorkomt.
\b
Komt overeen met een grens die een woord aangeeft, met andere woorden, de positie tussen een woord en een spatie. Bijvoorbeeld: "er\b" komt overeen met "er" in "lever", maar niet met "er" in "verbe".
\B
Komt overeen met een grens die geen woord vertegenwoordigt. "en*t\B" komt overeen met "ent" in "bien entendu".
\d
Komt overeen met een teken dat een cijfer vertegenwoordigt. Komt overeen met [0-9].
\D
Komt overeen met een teken dat geen cijfer vertegenwoordigt. Komt overeen met [^0-9].
\f
Komt overeen met een pagina-einde-teken.
\n
Komt overeen met een regeleinde-teken.
\r
Komt overeen met een carriage return-teken.
\s
Komt overeen met alle witruimte, inclusief spatie, tab, pagina-einde, enz. Komt overeen met "[ \f\n\r\t\v]".
\S
Komt overeen met elk niet-wit spatiekarakter. Komt overeen met "[^ \f\n\r\t\v]".
\t
Komt overeen met een tabteken.
\v
Komt overeen met een verticaal tabteken.
\w
Komt overeen met elk teken dat een woord vertegenwoordigt, inclusief een onderstrepingsteken. Komt overeen met "[A-Za-z0-9_]".
\W
Komt overeen met elk teken dat geen woord vertegenwoordigt. Komt overeen met "[^A-Za-z0-9_]".
\num
Komt overeen met num, waarbij num een positief geheel getal is. Verwijst naar opgeslagen overeenkomsten. Bijvoorbeeld: "(.)\1" komt overeen met twee opeenvolgende identieke tekens.
\n
Komt overeen met n, waarbij n een octale escape-waarde is. Octale escape-waarden moeten uit 1, 2 of 3 cijfers bestaan. Bijvoorbeeld: "\11" en "\011" komen beide overeen met een tab-teken. "\0011" is gelijk aan "\001" & "1". Octale escape-waarden mogen niet hoger zijn dan 256. Als dat wel het geval is, worden alleen de eerste twee cijfers in de uitdrukking meegenomen. Hiermee kunnen de codes ASCII in reguliere uitdrukkingen worden gebruikt.
\xn
Komt overeen met n, waarbij n een hexadecimale escape-waarde is. Hexadecimale escape-waarden moeten verplicht uit twee cijfers bestaan. Bijvoorbeeld: "\x41" komt overeen met "A". "\x041" is gelijk aan "\x04" & "1". Hiermee kunnen de codes ASCII in reguliere expressies worden gebruikt.

Een element in een sjabloon kan één of meerdere keren voorkomen. Laten we enkele voorbeelden bekijken rond het symbool \d, dat één cijfer vertegenwoordigt:

sjabloon
betekenis
\d
een cijfer
\d?
0 of 1 cijfer
\d*
0 of meer cijfers
\d+
1 of meer cijfers
\d{2}
2 cijfers
\d{3,}
minstens 3 cijfers
\d{5,7}
tussen 5 en 7 cijfers

Laten we nu eens kijken naar het patroon waarmee het verwachte formaat van een tekenreeks kan worden beschreven:

gezochte tekenreeks
model
een datum in de indeling dd/mm/jj
\d{2}/\d{2}/\d{2}
een tijd in de notatie hh:mm:ss
\d{2}:\d{2}:\d{2}
een geheel getal zonder teken
\d+
een reeks spaties, eventueel leeg
\s*
een geheel getal zonder teken dat voorafgegaan of gevolgd kan worden door spaties
\s*\d+\s*
een geheel getal dat een teken kan hebben en voorafgegaan of gevolgd kan worden door spaties
\s*[+|-]?\s*\d+\s*
een reëel getal zonder teken dat voorafgegaan of gevolgd kan worden door spaties
\s*\d+(.\d*)?\s*
een reëel getal dat een teken kan hebben en voorafgegaan of gevolgd kan worden door spaties
\s*[+|]?\s*\d+(.\d*)?\s*
een tekenreeks die het woord 'juste' bevat
\bjuste\b
  

Je kunt aangeven waar in de tekenreeks naar het patroon wordt gezocht:

patroon
betekenis
^patroon
het patroon begint in de tekenreeks
patroon$
het patroon beëindigt de reeks
^patroon$
het patroon begint en eindigt de reeks
patroon
het patroon wordt overal in de tekenreeks gezocht, te beginnen bij het begin ervan.
gezochte tekenreeks
patroon
een tekenreeks die eindigt met een uitroepteken
!$
een tekenreeks die eindigt op een punt
\.$
een tekenreeks die begint met de reeks //
^//
een tekenreeks die uit slechts één woord bestaat, eventueel gevolgd of voorafgegaan door spaties
^\s*\w+\s*$
een tekenreeks die bestaat uit twee woorden, eventueel voorafgegaan of gevolgd door spaties
^\s*\w+\s*\w+\s*$
een tekenreeks die het woord 'secret' bevat
\bsecret\b

De deelverzamelingen van een patroon kunnen worden "opgehaald". Zo kan niet alleen worden gecontroleerd of een tekenreeks overeenkomt met een bepaald patroon, maar kunnen ook de elementen in die tekenreeks worden opgehaald die overeenkomen met de deelverzamelingen van het patroon die tussen haakjes zijn geplaatst. Als we bijvoorbeeld een tekenreeks analyseren die een datum dd/mm/jj bevat en we willen bovendien de elementen dd, mm en jj van deze datum ophalen, dan gebruiken we het patroon (\d\d)/(\d\d)/(\d\d).

Laten we aan de hand van dit voorbeeld eens bekijken hoe we dit in VBScript uitvoeren.

  • We moeten eerst een object RegExp (Regular Expression) aanmaken
set modele=new regexp
  • Vervolgens stellen we het te testen patroon in
modele.pattern="(\d\d)/(\d\d)/(\d\d)"
  • Misschien wil je geen onderscheid maken tussen hoofdletters en kleine letters (standaard is dit wel het geval). Hier maakt het echter niet uit.
modele.IgnoreCase=true
  • Je kunt het patroon meerdere keren in de tekenreeks willen zoeken (standaard gebeurt dit niet)
modele.Global=true

Een globale zoekopdracht heeft alleen zin als het gebruikte patroon niet verwijst naar het begin of het einde van de tekenreeks.

  • We zoeken dan alle overeenkomsten van het patroon in de tekenreeks:
set correspondances=modele.execute(chaine)

De methode execute van een object RegExp retourneert een verzameling objecten van het type match. Dit object heeft een eigenschap value die het element van chaine is dat overeenkomt met het patroon. Als we modele.global=true hebben geschreven, kunnen er meerdere overeenkomsten zijn. Daarom is het resultaat van de methode execute een verzameling van overeenkomsten.

  • Het aantal overeenkomsten wordt weergegeven door correspondances.count. Als dit getal 0 is, betekent dit dat het sjabloon nergens is gevonden. De waarde van overeenkomst nr. i wordt weergegeven door correspondances(i).value. Als het patroon subpatronen tussen haakjes bevat, dan is het element van correspondances(i) dat overeenkomt met haakje j van het patroon gelijk aan overeenkomsten(i).submatches(j).

Dit alles wordt in het volgende voorbeeld geïllustreerd:

Programma
' reguliere expressie

' we willen controleren of een tekenreeks een datum in de indeling dd/mm/jj bevat

Option Explicit
Dim modele

' we definiëren het patroon
Set modele=new regexp
modele.pattern="\b(\d\d)/(\d\d)/(\d\d)\b"  ' une date n'importe où dans la chaîne
modele.global=true                      ' on recherchera le modèle plusieurs fois dans la chaîne

' de gebruiker voert de tekenreeks in waarin naar het patroon wordt gezocht
Dim chaine, correspondances, i

chaine=""
' we herhalen de lus zolang de tekenreeks niet gelijk is aan "einde"
Do While true
    ' de gebruiker wordt gevraagd tekst in te voeren
    wscript.stdout.writeLine "Tapez un texte contenant des dates au format jj/mm/aa et fin pour arrêter : "
    chaine=wscript.stdin.readLine
    ' stop als string = "einde"
    If chaine="fin" Then Exit Do
    ' de ingevoerde tekenreeks wordt vergeleken met het datummodel
    Set correspondances=modele.execute(chaine)
    ' is er een overeenkomst gevonden
    If correspondances.count<>0 Then
        ' er is ten minste één overeenkomst
        For i=0 To correspondances.count-1
            ' de overeenkomst wordt weergegeven i
            wscript.echo "J'ai trouvé la date " & correspondances(i).value
            ' de subelementen van de overeenkomst i worden opgehaald
            wscript.echo "Les éléments de la date " & i & " sont (" & correspondances(i).submatches(0) & "," _
            & correspondances(i).submatches(1) & "," & correspondances(i).submatches(2) & ")"
        Next
    Else
        ' geen overeenkomst
        wscript.echo "Je n'ai pas trouvé de date au format jj/mm/aa dans votre texte"
    End If
Loop

' klaar
wscript.quit 0
Resultaten
Tapez un texte contenant des dates au format jj/mm/aa et fin pour arrÛter :
aujourd'hui on est le 01/01/01 et demain sera le 02/01/02
J'ai trouvé la date 01/01/01
Les éléments de la date 0 sont (01,01,01)
J'ai trouvé la date 02/01/02
Les éléments de la date 1 sont (02,01,02)

Tapez un texte contenant des dates au format jj/mm/aa et fin pour arrÛter :
une date au format incorrect : 01/01/2002
Je n'ai pas trouvé de date au format jj/mm/aa dans votre texte

Tapez un texte contenant des dates au format jj/mm/aa et fin pour arrÛter :
une suite de dates : 10/10/10, 11/11/11, 12/12/12
J'ai trouvé la date 10/10/10
Les éléments de la date 0 sont (10,10,10)
J'ai trouvé la date 11/11/11
Les éléments de la date 1 sont (11,11,11)
J'ai trouvé la date 12/12/12
Les éléments de la date 2 sont (12,12,12)

Tapez un texte contenant des dates au format jj/mm/aa et fin pour arrÛter :
fin

Met reguliere expressies zou het programma dat controleert of een toetsaanslag inderdaad een positief geheel getal is, als volgt geschreven kunnen worden:

Programma
' gegevens worden ingelezen totdat deze als een getal worden herkend

Option Explicit

Dim fini, nombre

' het patroon van een positief geheel getal (dat echter nul kan zijn) wordt gedefinieerd
Dim modele
Set modele=new regexp
modele.pattern="^\s*\d+\s*$"

' er wordt een lus doorlopen zolang de ingevoerde gegevens niet correct zijn
' de lus wordt bestuurd door een booleaanse variabele die in het begin op 'false' staat (= nog niet voltooid)

fini=false
Do While Not fini
    ' het getal wordt opgevraagd
    wscript.stdout.write "Tapez un nombre entier >0: "
    ' het wordt ingelezen
    nombre=wscript.stdin.readLine
    ' het formaat van de ingelezen gegevens wordt gecontroleerd
    Dim correspondances
    Set correspondances=modele.execute(nombre)
    ' is het model gecontroleerd?
    If correspondances.count<>0 Then
        ' het is een geheel getal, maar is het >0?
        nombre=cint(nombre)
        If nombre>0 Then
            fini=true
        End If
    End If
    ' eventuele foutmelding
    If Not fini Then wscript.echo "Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp..."
Loop

' bevestiging
wscript.echo "Merci pour le nombre entier >0 : " & nombre

' en einde
wscript.quit 0
Resultaten
Tapez un nombre entier >0: 10.3
Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp...
Tapez un nombre entier >0: abcd
Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp...
Tapez un nombre entier >0: -4
Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp...
Tapez un nombre entier >0: 0
Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp...
Tapez un nombre entier >0: 1
Merci pour le nombre entier >0 : 1

Het vinden van de reguliere expressie waarmee we kunnen controleren of een tekenreeks aan een bepaald patroon voldoet, is soms een echte uitdaging. Met het volgende programma kun je hiermee oefenen. Het vraagt om een patroon en een tekenreeks en geeft vervolgens aan of de tekenreeks al dan niet aan het patroon voldoet.

Programma
' reguliere expressie

' we willen controleren of een tekenreeks overeenkomt met een patroon

Option Explicit

' we definiëren het patroon
Dim modele
Set modele=new regexp
modele.global=true                      ' on recherchera le modèle plusieurs fois dans la chaîne

' de gebruiker voert de tekenreeks in waarin naar het patroon wordt gezocht
Dim chaine, correspondances, i

Do While true
    ' de gebruiker wordt gevraagd een patroon in te voeren
    wscript.stdout.write "Tapez le modèle à tester et fin pour arrêter : "
    modele.pattern=wscript.stdin.readLine
    ' klaar?
    If modele.pattern="fin" Then Exit Do
        ' de gebruiker wordt gevraagd welke tekenreeksen met het sjabloon moeten worden vergeleken
        Do While true
            ' de gebruiker wordt gevraagd een patroon in te voeren
            wscript.stdout.writeLine "Tapez la chaîne à tester avec le modèle [" & modele.pattern & "] et fin pour arrêter : "
            chaine=wscript.stdin.readLine
            ' klaar?
            If chaine="fin" Then Exit Do
            ' de ingevoerde tekenreeks wordt vergeleken met het datummodel
            Set correspondances=modele.execute(chaine)
            ' is er een overeenkomst gevonden
            If correspondances.count<>0 Then
                ' er is ten minste één overeenkomst
                For i=0 To correspondances.count-1
                    ' de overeenkomst i wordt weergegeven
                    wscript.echo "J'ai trouvé la correspondance " & correspondances(i).value
                Next
            Else
                ' geen overeenkomst
                wscript.echo "Je n'ai pas trouvé de correspondance"
            End If
    Loop
Loop

' klaar
wscript.quit 0
Resultaten
Tapez le modèle à tester et fin pour arrêter : ^\s*\d+(\,\d+)*\s*$

Tapez la chaîne à tester avec le modèle [^\s*\d+(\,\d+)*\s*$] et fin pour arrêter :
18
J'ai trouvé la correspondance [18]

Tapez la chaîne à tester avec le modèle [^\s*\d+(\,\d+)*\s*$] et fin pour arrêter :
145.678
Je n'ai pas trouvé de correspondance

Tapez la chaîne à tester avec le modèle [^\s*\d+(\,\d+)*\s*$] et fin pour arrêter :
145,678
J'ai trouvé la correspondance [  145,678   ]

4.3. Uitvoeringsfouten opvangen

Een andere methode om uitvoeringsfouten af te handelen, is ze te laten plaatsvinden, erover geïnformeerd te worden en ze vervolgens af te handelen. Normaal gesproken geeft WSH bij een uitvoeringsfout een foutmelding weer en wordt het programma gestopt. Met twee instructies kunnen we deze werking wijzigen:

on error resume next

Deze instructie geeft het systeem (WSH) aan dat we de fouten zelf gaan afhandelen. Na deze instructie wordt elke fout gewoon door het systeem genegeerd.

on error goto 0

Deze instructie zet ons terug naar de normale foutafhandeling.

Wanneer de instructie on error resume next actief is, moeten we zelf de fouten afhandelen die kunnen optreden. Het object **Err** helpt ons daarbij. Dit object heeft verschillende eigenschappen en methoden, waarvan we de volgende twee onthouden:

  • number: een geheel getal dat het nummer aangeeft van de laatste fout die zich heeft voorgedaan. 0 betekent „geen fout“
  • description: de foutmelding die het systeem zou hebben weergegeven als we de instructie `on error resume next` niet hadden gebruikt

Laten we eens naar het volgende voorbeeld kijken:

Programma
Resultaten

' niet-afgehandelde fout

Option Explicit
Dim nombre

nombre=cdbl("abcd")
wscript.echo "nombre=" & nombre
C:\ err5.vbs(6, 1) Erreur d'exécution Microsoft VBScript: Type incompatible: 'cdbl'

Laten we nu de fout afhandelen:

Programma
Resultaten
' afgehandelde fout

Option Explicit
Dim nombre

' we behandelen de fouten zelf
On Error Resume Next
nombre=cdbl("abcd")
' is er een fout opgetreden?
If Err.number<>0 Then
    wscript.echo "L'erreur [" & err.description & "] s'est produite"
    On Error GoTo 0
    wscript.quit 1
End If
' geen fout – we keren terug naar de normale werking
On Error GoTo 0
wscript.echo "nombre=" & nombre
wscript.quit 0
L'erreur [Type incompatible] s'est produite

Laten we het programma voor het invoeren van een geheel getal >0 met deze nieuwe methode herschrijven:

Programma

' gegevens worden ingelezen totdat deze als een getal worden herkend

Option Explicit

Dim fini, nombre

' we herhalen de lus zolang de ingevoerde gegevens niet correct zijn
' de lus wordt bestuurd door een booleaanse variabele die in het begin op ‘false’ staat (= nog niet voltooid)

fini=false
Do While Not fini
  ' er wordt om het getal gevraagd
  wscript.stdout.write "Tapez un nombre entier >0: "
  ' het wordt ingelezen
  nombre=wscript.stdin.readLine
  ' het formaat van de ingelezen gegevens wordt gecontroleerd
  On Error Resume Next
  nombre=cdbl(nombre)
  If err.number=0 Then
    ' geen fout, het is een getal
    ' we keren terug naar de normale foutafhandeling
    On Error GoTo 0
    ' is het een geheel getal > 0
    If (nombre-int(nombre))=0 And nombre>0 Then
      fini=true
    End If
  End If
  ' we keren terug naar de normale foutafhandeling
  On Error GoTo 0
  ' eventuele foutmelding
  If Not fini Then wscript.echo "Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp..."
Loop

' bevestiging
wscript.echo "Merci pour le nombre entier >0 : " & nombre

' en einde
wscript.quit 0
Resultaten
Tapez un nombre entier >0: 4.5
Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp...
Tapez un nombre entier >0: 4,5
Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp...
Tapez un nombre entier >0: abcd
Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp...
Tapez un nombre entier >0: -4
Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp...
Tapez un nombre entier >0: 1
Merci pour le nombre entier >0 : 1

Opmerkingen:

  • Deze methode is soms de enige bruikbare. Vergeet dan niet om terug te keren naar de normale foutbehandelingsmodus zodra de reeks instructies die de fout kan veroorzaken, is voltooid.

4.4. Toepassing op het programma voor belastingberekening

We nemen het reeds geschreven programma voor belastingberekening opnieuw ter hand om ditmaal de geldigheid van de aan het programma doorgegeven argumenten te controleren:

Programma

' berekening van de belasting van een belastingplichtige
' het programma moet worden aangeroepen met drie parameters: gehuwd, kinderen, salaris
' getrouwd: letter O indien getrouwd, N indien ongehuwd
' kinderen: aantal kinderen
' salaris: jaarsalaris zonder centimes

' er wordt geen controle uitgevoerd op de geldigheid van de gegevens, maar
' wordt gecontroleerd of het er inderdaad drie zijn

' verplichte declaratie van variabelen
Option Explicit
Dim syntaxe
syntaxe= _
    "Syntaxe : pg marié enfants salaire" & vbCRLF & _
    "marié : caractère O si marié, N si non marié" & vbCRLF & _
    "enfants : nombre d'enfants (entier >=0)" & vbCRLF & _
    "salaire : salaire annuel sans les centimes (entier >=0)"

' er wordt gecontroleerd of er 3 argumenten zijn
  Dim nbArguments
  nbArguments=wscript.arguments.count
  If nbArguments<>3 Then
    ' foutmelding
    wscript.echo syntaxe & vbCRLF & vbCRLF & "erreur : nombre d'arguments incorrect"
    ' afbreken met foutcode 1
    wscript.quit 1
  End If
  
' de argumenten worden opgehaald en op geldigheid gecontroleerd
' een argument wordt zonder spaties ervoor en erachter aan het programma doorgegeven
' er worden reguliere expressies gebruikt om de geldigheid van de gegevens te controleren
  Dim modele, correspondances
  Set modele=new regexp

  ' de burgerlijke staat moet bestaan uit de tekens oOnN
  modele.pattern="^[oOnN]$"
  Set correspondances=modele.execute(wscript.arguments(0))
  If correspondances.count=0 Then
    ' fout
    wscript.echo syntaxe & vbCRLF & vbCRLF & "erreur : argument marie incorrect"
    ' we stoppen
    wscript.quit 2
  End If
  ' de waarde wordt opgehaald
  Dim marie
  If lcase(wscript.arguments(0)) = "o"Then
    marie=true
  Else
    marie=false
  End If
  
  ' 'kinderen' moet een geheel getal zijn >=0
  modele.pattern="^\d{1,2}$"
  Set correspondances=modele.execute(wscript.arguments(1))
  If correspondances.count=0 Then
    ' fout
    wscript.echo syntaxe & vbCRLF & vbCRLF & "erreur : argument enfants incorrect"
    ' afsluiten
    wscript.quit 3
  End If
  ' de waarde wordt opgehaald
  Dim enfants
  enfants=cint(wscript.arguments(1))

  ' salaris moet een geheel getal zijn >=0
  modele.pattern="^\d{1,9}$"
  Set correspondances=modele.execute(wscript.arguments(2))
  If correspondances.count=0 Then
    ' fout
    wscript.echo syntaxe & vbCRLF & vbCRLF & "erreur : argument salaire incorrect"
    ' afsluiten
    wscript.quit 4
  End If
  ' de waarde wordt opgehaald
  Dim salaire
  salaire=clng(wscript.arguments(2))
  
  ' de gegevens die nodig zijn voor de berekening van de belasting worden in 3 tabellen gedefinieerd
  Dim limites, coeffn, coeffr
  limites=array(12620,13190,15640,24740,31810,39970,48360, _
    55790,92970,127860,151250,172040,195000,0)
  coeffr=array(0,0.05,0.1,0.15,0.2,0.25,0.3,0.35,0.4,0.45, _
    0.5,0.55,0.6,0.65)
  coeffn=array(0,631,1290.5,2072.5,3309.5,4900,6898.5,9316.5, _
    12106,16754.5,23147.5,30710,39312,49062)

  ' het aantal aandelen wordt berekend
  Dim nbParts
  If marie=true Then
    nbParts=(enfants/2)+2
  Else
    nbParts=(enfants/2)+1
  End If
  If enfants>=3 Then nbParts=nbParts+0.5
  
  ' we berekenen het gezinsquotiënt en het belastbaar inkomen
  Dim revenu, qf
  revenu=0.72*salaire
  qf=revenu/nbParts
  
  ' de belasting wordt berekend
  Dim i, impot
  i=0
  Do While i<ubound(limites) And qf>limites(i)
    i=i+1
  Loop
  impot=int(revenu*coeffr(i)-nbParts*coeffn(i))
  
  ' het resultaat wordt weergegeven
  wscript.echo "impôt=" & impot
  
  ' wordt foutloos verlaten
  wscript.quit 0
Resultaten
C:\>cscript impots2.vbs

Syntaxe : pg marié enfants salaire
marié : caractère O si marié, N si non marié
enfants : nombre d'enfants (entier >=0)
salaire : salaire annuel sans les centimes (entier >=0)

erreur : nombre d'arguments incorrect

C:\>cscript impots2.vbs a b c

Syntaxe : pg marié enfants salaire
marié : caractère O si marié, N si non marié
enfants : nombre d'enfants (entier >=0)
salaire : salaire annuel sans les centimes (entier >=0)

erreur : argument marie incorrect


C:\>cscript impots2.vbs o b c

Syntaxe : pg marié enfants salaire
marié : caractère O si marié, N si non marié
enfants : nombre d'enfants (entier >=0)
salaire : salaire annuel sans les centimes (entier >=0)

erreur : argument enfants incorrect

C:\>cscript impots2.vbs o 2 c


Syntaxe : pg marié enfants salaire
marié : caractère O si marié, N si non marié
enfants : nombre d'enfants (entier >=0)
salaire : salaire annuel sans les centimes (entier >=0)

erreur : argument salaire incorrect

C:\>cscript impots2.vbs o 2 200000

impôt=22504