4. Obsługa błędów
W programowaniu obowiązuje bezwzględna zasada: program nigdy nie może „zawiesić się” w sposób niekontrolowany. Wszystkie błędy, które mogą wystąpić podczas wykonywania programu, muszą być obsługiwane, a także muszą być generowane odpowiednie komunikaty o błędach.
Jeśli powrócimy do omówionego wcześniej przykładu podatków, co się stanie, jeśli użytkownik wprowadzi dowolną wartość w polu liczby dzieci? Przyjrzyjmy się temu przykładowi:
| C:\>cscript impots1.vbs o xyzt 200000
C:\impots1.vbs(33, 3) Erreur d'exécution Microsoft VBScript: Type incompatible: 'cint'
|
To właśnie nazywamy niekontrolowanym awarią. Doszło do „awarii” w instrukcji dzieci=cint(wscript.arguments(1)), ponieważ arguments(1) zawierało ciąg znaków „xyzt”.
Przed użyciem wariantu, którego dokładnego charakteru nie znamy, należy sprawdzić jego dokładny podtyp. Można to zrobić na różne sposoby:
- sprawdzić rzeczywisty typ danych zawartych w wariancie za pomocą funkcji vartype lub typename
- użyć wyrażenia regularnego, aby sprawdzić, czy zawartość wariantu odpowiada określonemu wzorcowi
- pozwolić, by wystąpił błąd, a następnie go przechwycić i odpowiednio obsłużyć
Przyjrzyjmy się tym różnym metodom.
4.1. Określanie dokładnego typu danych
Przypomnijmy, że funkcje vartype lub varname pozwalają ustalić dokładny typ danych. Nie zawsze jest to jednak dla nas pomocne. Na przykład, gdy odczytujemy dane wprowadzone z klawiatury, funkcje vartype i typename poinformują nas, że jest to ciąg znaków, ponieważ tak traktowane są wszystkie dane wprowadzone z klawiatury. Nie mówi nam to jednak, czy ten ciąg znaków można na przykład uznać za prawidłową liczbę. Aby uzyskać dostęp do tego rodzaju informacji, używamy więc innych funkcji:
isNumeric(wyrażenie) | zwraca wartość true, jeśli wyrażenie może być użyte jako liczba |
isDate(wyrażenie) | zwraca wartość true, jeśli wyrażenie może być użyte jako data |
isEmpty(var) | zwraca wartość logiczną „prawda”, jeśli zmienna var nie została zainicjowana |
isNull(var) | zwraca wartość logiczną „prawda”, jeśli zmienna var zawiera nieprawidłowe dane |
isArray(var) | zwraca wartość true, jeśli var jest tablicą |
isObject(var) | zwraca wartość true, jeśli var jest obiektem |
Poniższy przykład prosi o wprowadzenie danych z klawiatury, dopóki nie zostaną one rozpoznane jako liczba:
Program |
' odczyt danych do momentu rozpoznania ich jako liczby
Option Explicit
Dim fini, nombre
' pętla trwa, dopóki wprowadzone dane nie będą poprawne
' pętla jest kontrolowana przez skończoną zmienną logiczną, ustawioną na „fałsz” na początku (= nie jest zakończona)
fini=false
Do While Not fini
' pyta się o liczbę
wscript.stdout.write "Tapez un nombre : "
' odczytuje się ją
nombre=wscript.stdin.readLine
' podczas odczytu typ danych jest z konieczności typu string
wscript.echo "Type de la donnée lue : " & typename(nombre) & "," & vartype(nombre)
' sprawdzamy rzeczywisty typ odczytanych danych
If isNumeric(nombre) Then
fini=true
Else
wscript.echo "Erreur, vous n'avez pas tapé un nombre. Recommencez svp..."
End If
Loop
' potwierdzenie
wscript.echo "Merci pour le nombre " & nombre
' i koniec
wscript.quit 0
|
Wyniki |
| 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
|
|
Funkcja isNumeric nie informuje nas, czy wyrażenie jest liczbą całkowitą, czy nie. Aby uzyskać tę informację, należy przeprowadzić dodatkowe testy. Poniższy przykład wymaga liczby całkowitej większej od 0:
Program |
' odczyt danych do momentu, aż zostaną one rozpoznane jako liczba całkowita >0
Option Explicit
Dim fini, nombre
' pętla trwa, dopóki wprowadzone dane nie są poprawne
' pętla jest kontrolowana przez skończoną zmienną logiczną, ustawioną początkowo na „fałsz” (= nie zakończono)
fini=false
Do While Not fini
' prosimy o podanie liczby
wscript.stdout.write "Tapez un nombre entier >0: "
' odczytuje się ją
nombre=wscript.stdin.readLine
' sprawdzamy, czy odczytana wartość jest typu zmiennoprzecinkowego
If isNumeric(nombre) Then
' czy jest to dodatnia liczba całkowita (liczba równa swojej części całkowitej)?
If (nombre-int(nombre))=0 And nombre>0 Then
fini=true
End If
End If
' ewentualny komunikat o błędzie
If Not fini Then wscript.echo "Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp..."
Loop
' potwierdzenie
wscript.echo "Merci pour le nombre entier >0 : " & nombre
' i koniec
wscript.quit 0
|
Wyniki |
| 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
|
|
Uwagi:
- int(nombre) zwraca część całkowitą liczby. Liczba równa swojej części całkowitej jest liczbą całkowitą.
- Warto zauważyć, że konieczne było zastosowanie testu If (liczba - int(liczba)) = 0 And liczba > 0, ponieważ test If liczba = int(liczba) And liczba > 0 nie dawał oczekiwanych wyników. Nie wykrywał on dodatnich liczb całkowitych. Odkrycie przyczyny tego zjawiska pozostawiamy czytelnikowi.
- Test If (liczba - int(liczba)) = 0 nie jest w pełni niezawodny. Przyjrzyjmy się poniższemu przykładowi wykonania:
Tapez un nombre entier >0: 4,0000000000000000000000001
Merci pour le nombre entier >0 : 4,0000000000000000000000001
|
Liczby rzeczywiste nie są reprezentowane dokładnie, lecz w przybliżeniu. W tym przypadku operacja nombre-int(nombre) dała wynik 0 z dokładnością wynikającą z możliwości komputera.
4.2. Wyrażenia regularne
Wyrażenia regularne pozwalają nam sprawdzić format ciągu znaków. Dzięki temu możemy zweryfikować, czy ciąg znaków reprezentujący datę ma format dd/mm/rr. W tym celu stosuje się wzorzec i porównuje się ciąg znaków z tym wzorcem. Tak więc w tym przykładzie j, m i a muszą być cyframi. Szablon prawidłowego formatu daty to zatem „\d\d/\d\d/\d\d”, gdzie symbol \d oznacza cyfrę. Symbole, których można używać w szablonie, są następujące (dokumentacja Microsoft):
Znak | Opis |
\ | Oznacza następny znak jako znak specjalny lub dosłowny. Na przykład „n” odpowiada znakowi „n”. „\n” odpowiada znakowi nowego wiersza. Sekwencja „\\” odpowiada znakowi „\”, natomiast „\(" odpowiada znakowi „(”. |
^ | Odpowiada początkowi wprowadzania tekstu. |
$ | Odpowiada końcowi wpisu. |
* | Odpowiada poprzedniemu znakowi zero lub więcej razy. Tak więc „zo*” odpowiada „z” lub „zoo”. |
+ | Odpowiada poprzedniemu znakowi jeden lub więcej razy. Na przykład „zo+” odpowiada „zoo”, ale nie odpowiada „z”. |
? | Odpowiada poprzedniemu znakowi zero lub jeden raz. Na przykład „a?ve?” odpowiada „ve” w słowie „lever”. |
. | Odpowiada dowolnemu pojedynczemu znakowi, z wyjątkiem znaku nowej linii. |
(wzór) | Wyszukuje ciąg modèle i zapisuje dopasowanie. Odpowiedni podciąg można wyodrębnić z uzyskanej kolekcji Matches za pomocą elementu [0]...[n]. Aby znaleźć dopasowania zawierające znaki w nawiasach ( ), należy użyć znaków „\(" lub „\)”. |
x|y | Odpowiada albo x, albo y. Na przykład „z|foot" correspond à "z" ou à "foot". "(z|f)oo” odpowiada „zoo” lub „foo”. |
{n} | n jest liczbą całkowitą nieujemną. Odpowiada dokładnie n razy danemu znakowi. Na przykład „o{2}” nie odpowiada znakowi „o” w słowie „Bob”, ale dwóm pierwszym znakom „o” w słowie „fooooot”. |
{n,} | n jest liczbą całkowitą nieujemną. Odpowiada co najmniej n-krotności danego znaku. Na przykład „o{2,}” nie odpowiada literze „o” w słowie „Bob”, ale wszystkim literom „o” w słowie „fooooot”. „o{1,}” jest równoważne „o+”, a „o{0,}” jest równoważne „o*”. |
{n,m} | m i n to liczby całkowite nieujemne. Odpowiada co najmniej n i co najwyżej m wystąpieniom znaku. Na przykład „o{1,3}” odpowiada trzem pierwszym „o” w słowie „foooooot”, a „o{0,1}” odpowiada „o?”. |
[xyz] | Zbiór znaków. Odpowiada jednemu z podanych znaków. Na przykład „[abc]” odpowiada znakowi „a” w słowie „plat”. |
[^xyz] | Zbiór znaków ujemnych. Odpowiada każdemu znakowi, który nie został wymieniony. Na przykład „[^abc]” odpowiada literze „p” w słowie „plat”. |
[a-z] | Zakres znaków. Odpowiada dowolnemu znakowi z podanego zakresu. Na przykład „[a-z]” odpowiada dowolnemu małej literze alfabetu od „a” do „z”. |
[^m-z] | Zakres znaków ujemny. Odpowiada dowolnemu znakowi, który nie występuje w podanej serii. Na przykład „[^m-z]” odpowiada dowolnemu znakowi, który nie występuje między „m” a „z”. |
\b | Odpowiada granicy wyznaczającej słowo, czyli pozycji między słowem a spacją. Na przykład „er\b” odpowiada „er” w słowie „lever”, ale nie odpowiada „er” w słowie „verbe”. |
\B | Odpowiada granicy nieoznaczającej słowa. „en*t\B” odpowiada „ent” w słowie „bien entendu”. |
\d | Odpowiada znakowi reprezentującemu cyfrę. Odpowiada [0-9]. |
\D | Odpowiada znakowi, który nie jest cyfrą. Odpowiada ciągowi [^0-9]. |
\f | Odpowiada znakowi końca strony. |
\n | Odpowiada znakowi nowego wiersza. |
\r | Odpowiada znakowi powrotu karetki. |
\s | Odpowiada dowolnemu znakowi spacji, w tym spacji, tabulacji, znakowi końca strony itp. Odpowiada ciągowi znaków „[ \f\n\r\t\v]”. |
\S | Odpowiada dowolnemu znakowi niebędącemu spacją. Odpowiada „[^ \f\n\r\t\v]”. |
\t | Odpowiada znakowi tabulacji. |
\v | Odpowiada znakowi pionowego tabulacji. |
\w | Odpowiada dowolnemu znakowi reprezentującemu słowo, w tym znakowi podkreślenia. Odpowiada ciągowi znaków „[A-Za-z0-9_]”. |
\W | Odpowiada dowolnemu znakowi, który nie stanowi słowa. Odpowiada ciągowi „[^A-Za-z0-9_]”. |
\num | Odpowiada num, gdzie num jest dodatnią liczbą całkowitą. Odnosi się do zapisanych dopasowań. Na przykład „(.)\1” odpowiada dwóm identycznym, następującym po sobie znakom. |
\n | Odpowiada n, gdzie n jest ośmiokrotną wartością escape. Ośmiokrotne wartości escape muszą składać się z 1, 2 lub 3 cyfr. Na przykład zarówno „\11”, jak i „\011” odpowiadają znakowi tabulacji. „\0011” jest równoważne „\001” i „1”. Oktalowe wartości escape nie mogą przekraczać 256. W przeciwnym razie w wyrażeniu brane pod uwagę będą tylko dwie pierwsze cyfry. Umożliwia używanie kodów ASCII w wyrażeniach regularnych. |
\xn | Odpowiada n, gdzie n jest szesnastkową wartością escape. Wartości ucieczkowe w systemie szesnastkowym muszą obowiązkowo składać się z dwóch cyfr. Na przykład „\x41” odpowiada literze „A”. „\x041” jest równoważne „\x04” i „1”. Umożliwia stosowanie kodów ASCII w wyrażeniach regularnych. |
Element w szablonie może występować w jednej lub wielu instancjach. Rozważmy kilka przykładów dotyczących symbolu \d, który reprezentuje jedną cyfrę:
szablon | znaczenie |
\d | jedna cyfra |
\d? | 0 lub 1 cyfra |
\d* | 0 lub więcej cyfr |
\d+ | 1 lub więcej cyfr |
\d{2} | 2 cyfry |
\d{3,} | co najmniej 3 cyfry |
\d{5,7} | od 5 do 7 cyfr |
Wyobraźmy sobie teraz wzorzec, który może opisać oczekiwany format ciągu znaków:
poszukiwany ciąg znaków | model |
data w formacie dd/mm/rr | \d{2}/\d{2}/\d{2} |
godzina w formacie hh:mm:ss | \d{2}:\d{2}:\d{2} |
liczba całkowita bez znaku | \d+ |
sekwencja spacji, która może być pusta | \s* |
liczba całkowita bez znaku, przed którą lub po której mogą występować spacje | \s*\d+\s* |
liczba całkowita, która może być ze znakiem i może być poprzedzona lub następować po spacjach | \s*[+|-]?\s*\d+\s* |
liczba rzeczywista bez znaku, przed którą lub po której mogą występować spacje | \s*\d+(.\d*)?\s* |
liczba rzeczywista, która może być ze znakiem i może być poprzedzona lub następowana przez spacje | \s*[+|]?\s*\d+(.\d*)?\s* |
ciąg znaków zawierający słowo „juste” | \bjuste\b |
| | |
Można określić, w którym miejscu ciągu ma być wyszukiwany wzorzec:
wzór | znaczenie |
^wzorzec | wzorzec rozpoczyna ciąg |
wzór$ | szablon kończy ciąg |
^wzorzec$ | szablon rozpoczyna i kończy ciąg |
wzorzec | wzorzec jest wyszukiwany w całym ciągu, zaczynając od jego początku. |
wyszukiwany ciąg | wzorzec |
ciąg kończący się wykrzyknikiem | !$ |
ciąg kończący się kropką | \.$ |
ciąg znaków rozpoczynający się sekwencją // | ^// |
ciąg zawierający tylko jedno słowo, po którym mogą następować lub przed którym mogą występować spacje | ^\s*\w+\s*$ |
ciąg zawierający dwa słowa, po których mogą następować lub przed którymi mogą występować spacje | ^\s*\w+\s*\w+\s*$ |
ciąg zawierający słowo „secret” | \bsecret\b |
Podzbiory wzorca można „wyodrębnić”. Dzięki temu nie tylko można sprawdzić, czy ciąg znaków odpowiada konkretnemu wzorcowi, ale także wyodrębnić z tego ciągu elementy odpowiadające podzbiorom wzorca, które zostały ujęte w nawiasy. Jeśli więc analizujemy ciąg zawierający datę dd/mm/rr i chcemy dodatkowo wyodrębnić elementy dd, mm, rr z tej daty, użyjemy wzorca (\d\d)/(\d\d)/(\d\d).
Zobaczmy na tym przykładzie, jak to zrobić w języku VBScript.
- Najpierw musimy utworzyć obiekt RegExp (wyrażenie regularne)
- Następnie ustalamy wzorzec do przetestowania
modele.pattern="(\d\d)/(\d\d)/(\d\d)"
- Możemy zrezygnować z rozróżniania wielkich i małych liter (domyślnie jest ono włączone). W tym przypadku nie ma to żadnego znaczenia.
- można chcieć wyszukać wzorzec kilka razy w ciągu znaków (domyślnie nie jest to robione)
Wyszukiwanie globalne ma sens tylko wtedy, gdy używany wzorzec nie odnosi się do początku ani końca ciągu znaków.
- W takim przypadku wyszukuje się wszystkie wystąpienia wzorca w ciągu:
set correspondances=modele.execute(chaine)
Metoda execute obiektu RegExp zwraca zbiór obiektów typu match. Obiekt ten posiada właściwość value, która jest elementem chaine odpowiadającym wzorcowi. Jeśli wpisano modele.global=true, może istnieć wiele dopasowań. Dlatego wynikiem metody execute jest zbiór dopasowań.
- Liczba dopasowań jest określona przez correspondances.count. Jeśli liczba ta wynosi 0, oznacza to, że szablon nie został nigdzie znaleziony. Wartość dopasowania nr i jest określona przez correspondances(i).value. Jeśli wzorzec zawiera podwzorce w nawiasach, wówczas element correspondances(i) odpowiadający nawiasowi j we wzorcu to correspondances(i).submatches(j).
Wszystko to pokazano w poniższym przykładzie:
Program |
' wyrażenie regularne
' chcemy sprawdzić, czy ciąg znaków zawiera datę w formacie dd/mm/rr
Option Explicit
Dim modele
' definiujemy wzorzec
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
' to użytkownik podaje ciąg znaków, w którym będziemy szukać wzorca
Dim chaine, correspondances, i
chaine=""
' pętla trwa tak długo, jak długo ciąg znaków nie jest równy „koniec”
Do While true
' prosi się użytkownika o wpisanie tekstu
wscript.stdout.writeLine "Tapez un texte contenant des dates au format jj/mm/aa et fin pour arrêter : "
chaine=wscript.stdin.readLine
' kończymy, jeśli ciąg znaków = „koniec”
If chaine="fin" Then Exit Do
' porównujemy odczytany ciąg znaków z wzorcem daty
Set correspondances=modele.execute(chaine)
' czy znaleziono dopasowanie
If correspondances.count<>0 Then
' znaleziono co najmniej jedno dopasowanie
For i=0 To correspondances.count-1
' wyświetla się wpis i
wscript.echo "J'ai trouvé la date " & correspondances(i).value
' pobieramy podelementy dopasowania i
wscript.echo "Les éléments de la date " & i & " sont (" & correspondances(i).submatches(0) & "," _
& correspondances(i).submatches(1) & "," & correspondances(i).submatches(2) & ")"
Next
Else
' brak dopasowania
wscript.echo "Je n'ai pas trouvé de date au format jj/mm/aa dans votre texte"
End If
Loop
' koniec
wscript.quit 0
|
Wyniki |
| 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
|
|
Wykorzystując wyrażenia regularne, program sprawdzający, czy wartość wprowadzona z klawiatury jest liczbą całkowitą dodatnią, można zapisać w następujący sposób:
Program |
' odczyt danych do momentu rozpoznania ich jako liczby
Option Explicit
Dim fini, nombre
' definiujemy wzór liczby całkowitej dodatniej (która jednak może wynosić zero)
Dim modele
Set modele=new regexp
modele.pattern="^\s*\d+\s*$"
' pętla trwa, dopóki wprowadzone dane nie są poprawne
' pętla jest kontrolowana przez skończoną zmienną logiczną, której wartość na początku jest ustawiona na „fałsz” (= proces nie został zakończony)
fini=false
Do While Not fini
' prosimy o podanie liczby
wscript.stdout.write "Tapez un nombre entier >0: "
' odczytuje się ją
nombre=wscript.stdin.readLine
' sprawdzamy format odczytanych danych
Dim correspondances
Set correspondances=modele.execute(nombre)
' czy model został zweryfikowany?
If correspondances.count<>0 Then
' to liczba całkowita, ale czy jest >0?
nombre=cint(nombre)
If nombre>0 Then
fini=true
End If
End If
' ewentualny komunikat o błędzie
If Not fini Then wscript.echo "Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp..."
Loop
' potwierdzenie
wscript.echo "Merci pour le nombre entier >0 : " & nombre
' i koniec
wscript.quit 0
|
Wyniki |
| 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
|
|
Znalezienie wyrażenia regularnego, które pozwoli nam sprawdzić, czy dany ciąg znaków odpowiada określonemu wzorcowi, bywa prawdziwym wyzwaniem. Poniższy program pozwala poćwiczyć tę umiejętność. Wymaga on podania wzorca i ciągu znaków, a następnie wskazuje, czy ciąg ten odpowiada wzorcowi, czy nie.
Program |
' wyrażenie regularne
' chcemy sprawdzić, czy ciąg znaków pasuje do wzorca
Option Explicit
' definiujemy wzorzec
Dim modele
Set modele=new regexp
modele.global=true ' on recherchera le modèle plusieurs fois dans la chaîne
' to użytkownik podaje ciąg znaków, w którym będziemy szukać wzorca
Dim chaine, correspondances, i
Do While true
' użytkownik jest proszony o wpisanie wzorca
wscript.stdout.write "Tapez le modèle à tester et fin pour arrêter : "
modele.pattern=wscript.stdin.readLine
' Skończono?
If modele.pattern="fin" Then Exit Do
' użytkownik jest proszony o podanie ciągów znaków, które mają zostać porównane z wzorcem
Do While true
' użytkownik proszony jest o wpisanie wzorca
wscript.stdout.writeLine "Tapez la chaîne à tester avec le modèle [" & modele.pattern & "] et fin pour arrêter : "
chaine=wscript.stdin.readLine
' zakończone?
If chaine="fin" Then Exit Do
' porównuje się odczytany ciąg znaków z wzorcem daty
Set correspondances=modele.execute(chaine)
' czy znaleziono dopasowanie
If correspondances.count<>0 Then
' znaleziono co najmniej jedno dopasowanie
For i=0 To correspondances.count-1
' wyświetla się dopasowanie i
wscript.echo "J'ai trouvé la correspondance " & correspondances(i).value
Next
Else
' brak dopasowania
wscript.echo "Je n'ai pas trouvé de correspondance"
End If
Loop
Loop
' zakończono
wscript.quit 0
|
Wyniki |
| 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. Przechwytywanie błędów wykonania
Inną metodą obsługi błędów wykonania jest dopuszczenie do ich wystąpienia, otrzymanie powiadomienia o nich, a następnie ich obsługa. Zazwyczaj, gdy podczas wykonywania programu wystąpi błąd, WSH wyświetla komunikat o błędzie, a program zostaje zatrzymany. Dwie instrukcje pozwalają nam zmienić to zachowanie:
Ta instrukcja informuje system (WSH), że sami zajmiemy się obsługą błędów. Po tej instrukcji wszelkie błędy są po prostu ignorowane przez system.
Ta instrukcja przywraca normalne działanie obsługi błędów.
Gdy instrukcja „on error resume next” jest aktywna, musimy samodzielnie obsługiwać błędy, które mogą wystąpić. Pomaga nam w tym obiekt Err. Obiekt ten posiada różne właściwości i metody, z których zwrócimy uwagę na dwie następujące:
- number: liczba całkowita oznaczająca numer ostatniego błędu, który wystąpił. 0 oznacza „brak błędu”
- description: komunikat o błędzie, który wyświetliłby system, gdyby nie wydano instrukcji „on error resume next”
Spójrzmy na poniższy przykład:
Program | Wyniki |
' nieobsługiwany błąd
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'
|
|
Zajmijmy się teraz błędem:
Program | Wyniki |
' błąd obsłużony
Option Explicit
Dim nombre
' samodzielnie obsługujemy błędy
On Error Resume Next
nombre=cdbl("abcd")
' czy wystąpił błąd?
If Err.number<>0 Then
wscript.echo "L'erreur [" & err.description & "] s'est produite"
On Error GoTo 0
wscript.quit 1
End If
' brak błędu – powrót do normalnego działania
On Error GoTo 0
wscript.echo "nombre=" & nombre
wscript.quit 0
|
| L'erreur [Type incompatible] s'est produite
|
|
Przepiszmy program do wprowadzania liczby całkowitej >0, wykorzystując tę nową metodę:
Program |
' odczyt danych do momentu, aż zostaną one rozpoznane jako liczba
Option Explicit
Dim fini, nombre
' pętla trwa, dopóki wprowadzone dane nie będą poprawne
' pętla jest kontrolowana przez zmienną logiczną o wartości skończonej, ustawioną na „fałsz” na początku (= nie zakończono)
fini=false
Do While Not fini
' pyta się o liczbę
wscript.stdout.write "Tapez un nombre entier >0: "
' odczytuje się ją
nombre=wscript.stdin.readLine
' sprawdzamy format odczytanych danych
On Error Resume Next
nombre=cdbl(nombre)
If err.number=0 Then
' brak błędu – to liczba
' powrót do normalnego trybu obsługi błędów
On Error GoTo 0
' czy jest to liczba całkowita >0
If (nombre-int(nombre))=0 And nombre>0 Then
fini=true
End If
End If
' powrót do normalnego trybu obsługi błędów
On Error GoTo 0
' ewentualny komunikat o błędzie
If Not fini Then wscript.echo "Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp..."
Loop
' potwierdzenie
wscript.echo "Merci pour le nombre entier >0 : " & nombre
' i koniec
wscript.quit 0
|
Wyniki |
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
|
Uwagi:
- Ta metoda jest czasami jedyną możliwą do zastosowania. Nie należy wówczas zapomnieć o powrocie do normalnego trybu obsługi błędów, gdy tylko sekwencja instrukcji, która może wygenerować błąd, zostanie zakończona.
4.4. Zastosowanie w programie do obliczania podatków
Wracamy do już napisanego programu do obliczania podatków, aby tym razem sprawdzić poprawność argumentów przekazanych do programu:
Program |
' obliczenie podatku podatnika
' program należy wywołać z trzema parametrami: stan cywilny, liczba dzieci, wynagrodzenie
' stan cywilny: znak O, jeśli jest żonaty, N, jeśli jest niezamężny
' dzieci: liczba dzieci
' wynagrodzenie: roczne wynagrodzenie bez centów
' nie przeprowadza się żadnej weryfikacji poprawności danych, ale
' sprawdza się, czy jest ich rzeczywiście trzech
' obowiązkowe zadeklarowanie zmiennych
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)"
' sprawdza się, czy są 3 argumenty
Dim nbArguments
nbArguments=wscript.arguments.count
If nbArguments<>3 Then
' komunikat o błędzie
wscript.echo syntaxe & vbCRLF & vbCRLF & "erreur : nombre d'arguments incorrect"
' zatrzymanie z kodem błędu 1
wscript.quit 1
End If
' pobieranie argumentów z weryfikacją ich poprawności
' argument jest przekazywany do programu bez spacji przed i po nim
' do sprawdzania poprawności danych zostaną użyte wyrażenia regularne
Dim modele, correspondances
Set modele=new regexp
' stan cywilny musi składać się z znaków zawartych w zestawie oOnN
modele.pattern="^[oOnN]$"
Set correspondances=modele.execute(wscript.arguments(0))
If correspondances.count=0 Then
' błąd
wscript.echo syntaxe & vbCRLF & vbCRLF & "erreur : argument marie incorrect"
' kończymy
wscript.quit 2
End If
' pobieramy wartość
Dim marie
If lcase(wscript.arguments(0)) = "o"Then
marie=true
Else
marie=false
End If
' liczba dzieci musi być liczbą całkowitą >=0
modele.pattern="^\d{1,2}$"
Set correspondances=modele.execute(wscript.arguments(1))
If correspondances.count=0 Then
' błąd
wscript.echo syntaxe & vbCRLF & vbCRLF & "erreur : argument enfants incorrect"
' kończymy
wscript.quit 3
End If
' pobieramy wartość
Dim enfants
enfants=cint(wscript.arguments(1))
' wynagrodzenie musi być liczbą całkowitą >=0
modele.pattern="^\d{1,9}$"
Set correspondances=modele.execute(wscript.arguments(2))
If correspondances.count=0 Then
' błąd
wscript.echo syntaxe & vbCRLF & vbCRLF & "erreur : argument salaire incorrect"
' kończymy
wscript.quit 4
End If
' pobieramy wartość
Dim salaire
salaire=clng(wscript.arguments(2))
' definiuje się dane niezbędne do obliczenia podatku w 3 tabelach
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)
' obliczamy liczbę udziałów
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
' obliczamy współczynnik rodzinny i dochód podlegający opodatkowaniu
Dim revenu, qf
revenu=0.72*salaire
qf=revenu/nbParts
' oblicza się podatek
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))
' wyświetla się wynik
wscript.echo "impôt=" & impot
' wychodzimy bez błędu
wscript.quit 0
|
Wyniki |
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
|