Skip to content

4. معالجة الأخطاء

في البرمجة، هناك قاعدة مطلقة واحدة: يجب ألا "يتعطل" البرنامج أبدًا بشكل غير متوقع. يجب معالجة جميع الأخطاء التي قد تحدث أثناء تنفيذ البرنامج، ويجب إنشاء رسائل خطأ ذات معنى.

إذا عدنا إلى مثال الضريبة الذي تمت مناقشته سابقًا، ماذا يحدث إذا أدخل المستخدم أي قيمة لعدد الأطفال؟ لنلقِ نظرة على هذا المثال:

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

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

هذا ما يُعرف بـ "التعطل المفاجئ". تسببت التعليمات enfants=cint(wscript.arguments(1)) في "تعطل" لأن arguments(1) احتوت على السلسلة "xyzt".

قبل استخدام متغير لا تُعرف طبيعته بالضبط، يجب عليك التحقق من نوعه الفرعي بالضبط. هناك عدة طرق للقيام بذلك:

  • اختبار النوع الفعلي للبيانات الموجودة في متغير باستخدام دالتي vartype أو typename
  • استخدام تعبير عادي للتحقق من أن محتوى المتغير يطابق نمطًا معينًا
  • السماح بحدوث الخطأ، ثم اعتراضه ومعالجته

سنقوم بفحص هذه الطرق المختلفة.

4.1. تحديد النوع الدقيق لعنصر البيانات

تذكر أن دالتي vartype و varname تسمحان لك بتحديد النوع الدقيق لقطعة من البيانات. هذا ليس مفيدًا دائمًا. على سبيل المثال، عندما نقرأ البيانات المكتوبة على لوحة المفاتيح، ستخبرنا الدالتان vartype و typename أنها سلسلة، لأن جميع البيانات المكتوبة على لوحة المفاتيح تُعامل على هذا النحو. وهذا لا يخبرنا ما إذا كان يمكن، على سبيل المثال، اعتبار هذه السلسلة رقمًا صالحًا. لذلك نستخدم دوال أخرى للوصول إلى هذا النوع من المعلومات:

isNumeric(expression)
تُرجع القيمة true إذا كان التعبير يمكن استخدامه كرقم
isDate(expression)
تُرجع القيمة true إذا كان التعبير يمكن استخدامه كتاريخ
isEmpty(var)
تُرجع القيمة true إذا لم يتم تهيئة المتغير var
isNull(var)
تُرجع القيمة true إذا كان المتغير var يحتوي على بيانات غير صالحة
isArray(var)
تُرجع القيمة true إذا كان var عبارة عن مصفوفة
isObject(var)
تُرجع القيمة true إذا كان var كائنًا

يطلب المثال التالي من المستخدم إدخال البيانات عبر لوحة المفاتيح حتى يتم التعرف عليها كرقم:

البرنامج

' read data until it is recognized as a number

Option Explicit

Dim fini, nombre

' loop until the data entered is correct
' the loop is controlled by a finite boolean, set to false at the start (= it's not finite)

fini=false
Do While Not fini
    ' we ask for the number
    wscript.stdout.write "Tapez un nombre : "
    ' we read it
    nombre=wscript.stdin.readLine
    ' the type is necessarily string when read
    wscript.echo "Type de la donnée lue : " & typename(nombre) & "," & vartype(nombre)
    ' test the actual type of data read
    If isNumeric(nombre) Then
        fini=true
    Else
        wscript.echo "Erreur, vous n'avez pas tapé un nombre. Recommencez svp..."
    End If
Loop

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

' and end
wscript.quit 0

النتائج

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

لا تخبرنا الدالة isNumeric ما إذا كان التعبير عددًا صحيحًا أم لا. للحصول على هذه المعلومات، يلزم إجراء اختبارات إضافية. يطلب المثال التالي عددًا صحيحًا أكبر من 0:

البرنامج

' read data until it is recognized as an integer >0

Option Explicit

Dim fini, nombre

' loop until the data entered is correct
' the loop is controlled by a finite boolean, set to false at the start (= it's not finite)

fini=false
Do While Not fini
    ' we ask for the number
    wscript.stdout.write "Tapez un nombre entier >0: "
    ' we read it
    nombre=wscript.stdin.readLine
    ' test the actual type of data read
    If isNumeric(nombre) Then
        ' is it a positive integer (number equal to its integer part)?
        If (nombre-int(nombre))=0 And nombre>0 Then
            fini=true
        End If
    End If
    ' possible error msg
    If Not fini Then wscript.echo "Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp..."
Loop

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

' and end
wscript.quit 0

النتائج

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

تعليقات:

  • تُرجع الدالة int(number) الجزء الصحيح من الرقم. والرقم الذي يساوي الجزء الصحيح منه هو عدد صحيح.
  • من المثير للاهتمام ملاحظة أننا اضطررنا إلى استخدام الاختبار If (number - int(number)) = 0 And number > 0 لأن الاختبار If number = int(number) And number > 0 لم يسفر عن النتائج المتوقعة. لم يكتشف الأعداد الصحيحة الموجبة. نترك للقارئ اكتشاف السبب.
  • اختبار If (number - int(number)) = 0 ليس موثوقًا تمامًا. لنلقِ نظرة على المثال التالي:
أدخل عددًا صحيحًا >0: 4.0000000000000000000000001

شكرًا على العدد الصحيح >0: 4.0000000000000000000000001

لا يتم تمثيل الأعداد الحقيقية بدقة بل تقريبياً. وهنا، أعادت العملية number-int(number) القيمة 0 بدقة الحاسوب.

4.2. التعبيرات العادية

تسمح لنا التعبيرات العادية باختبار تنسيق سلسلة. وبهذه الطريقة، يمكننا التحقق من أن السلسلة التي تمثل تاريخًا هي بتنسيق dd/mm/yy. للقيام بذلك، نستخدم نمطًا ونقارن السلسلة بهذا النمط. وبالتالي، في هذا المثال، يجب أن تكون d و m و y أرقامًا. يكون النمط لتنسيق تاريخ صالح هو "\d\d/\d\d/\d\d"، حيث يشير الرمز \d إلى رقم. الرموز التي يمكن استخدامها في النمط هي كما يلي (وثائق Microsoft):

الحرف
الوصف
\
يشير إلى الحرف التالي كحرف خاص أو حرفي. على سبيل المثال، "n" يقابل الحرف "n". "\n" يقابل حرف السطر الجديد. التسلسل "\\" يقابل "\"، بينما "\(" يقابل "(".
^
يطابق بداية الإدخال.
$
يتطابق مع نهاية الإدخال.
*
يتطابق مع الحرف السابق صفر أو أكثر من مرة. وبالتالي، فإن "zo*" يتطابق مع "z" أو "zoo".
+
يتطابق مع الحرف السابق مرة واحدة أو أكثر. وبالتالي، فإن "zo+" يتطابق مع "zoo"، ولكنه لا يتطابق مع "z".
?
يتطابق مع الحرف السابق صفر أو مرة واحدة. على سبيل المثال، "a?ve?" يتطابق مع "ve" في "lever".
.
يتطابق مع أي حرف واحد، باستثناء حرف السطر الجديد.
(النمط)
يبحث عن النمط ويخزن المطابقة. يمكن استرداد السلسلة الفرعية المطابقة من مجموعة Matches الناتجة باستخدام Item [0]...[n]. للعثور على المطابقات التي تحتوي على أحرف داخل أقواس ( )، استخدم "\(" أو "\)".
x|y
يطابق إما x أو y. على سبيل المثال، "z|foot" يطابق "z" أو "foot". "(z|f)oo" يطابق "zoo" أو "foo".
{n}
n هو عدد صحيح غير سالب. يطابق n مرة بالضبط من ظهور الحرف. على سبيل المثال، "o{2}" لا يطابق "o" في "Bob"، ولكنه يطابق أول حرفين "o" في "fooooot".
{n,}
n هو عدد صحيح غير سالب. يطابق n مرة على الأقل من ظهور الحرف. على سبيل المثال، لا يطابق "o{2,}" حرف "o" في كلمة "Bob"، ولكنه يطابق جميع أحرف "o" في كلمة "fooooot". "o{1,}" يعادل "o+" و"o{0,}" يعادل "o*".
{n,m}
m و n عددان صحيحان غير سالبين. يطابق ما لا يقل عن n وما لا يزيد عن m من تكرارات الحرف. على سبيل المثال، "o{1,3}" يطابق الأحرف "o" الثلاثة الأولى في "foooooot"، و "o{0,1}" يطابق "o?".
[xyz]
مجموعة أحرف. يطابق أيًا من الأحرف المحددة. على سبيل المثال، يطابق "[abc]" الحرف "a" في "plat".
[^xyz]
مجموعة أحرف سلبية. تتطابق مع أي حرف غير مدرج. على سبيل المثال، "[^abc]" تتطابق مع الحرف "p" في كلمة "plat".
[a-z]
نطاق الأحرف. يطابق أي حرف في النطاق المحدد. على سبيل المثال، يطابق "[a-z]" أي حرف أبجدي صغير بين "a" و "z".
[^m-z]
نطاق الأحرف السلبية. يطابق أي حرف غير موجود في النطاق المحدد. على سبيل المثال، يطابق "[^m-z]" أي حرف غير موجود بين "m" و "z".
\b
يتطابق مع حدود الكلمة، أي الموضع بين الكلمة والمسافة. على سبيل المثال، يتطابق "er\b" مع "er" في "lever"، ولكنه لا يتطابق مع "er" في "verb".
\B
يتطابق مع حد لا يمثل كلمة. يتطابق "en*t\B" مع "ent" في "bien entendu".
\d
يتطابق مع حرف يمثل رقمًا. يعادل [0-9].
\D
يتطابق مع حرف ليس رقمًا. يعادل [^0-9].
\f
يتطابق مع حرف فاصل الأسطر.
\n
يتطابق مع حرف السطر الجديد.
\r
مكافئ لحرف إرجاع الحامل.
\s
يتطابق مع أي مسافة بيضاء، بما في ذلك المسافة، وعلامة الجدولة، وفاصل الصفحة، وما إلى ذلك. يعادل "[ \f\n\r\t\v]".
\S
يتطابق مع أي حرف غير مسافة بيضاء. يعادل "[^ \f\n\r\t\v]".
\t
يتطابق مع حرف الجدولة.
\v
يتطابق مع حرف الجدولة الرأسية.
\w
يتطابق مع أي حرف يمثل كلمة ويشمل شرطة سفلية. يعادل "[A-Za-z0-9_]".
\W
يتطابق مع أي حرف لا يمثل كلمة. يعادل "[^A-Za-z0-9_]".
\num
يتطابق مع num، حيث num هو عدد صحيح موجب. يشير إلى التطابقات المخزنة. على سبيل المثال، يتطابق "(.)\1" مع حرفين متتاليين متطابقين.
\n
يتطابق مع n، حيث n هو قيمة هروب ثمانية. يجب أن تتكون قيم الهروب الثمانية من 1 أو 2 أو 3 أرقام. على سبيل المثال، يتطابق كل من "\11" و "\011" مع حرف الجدولة. "\0011" يعادل "\001" و "1". يجب ألا تتجاوز قيم الهروب الثمانية 256. إذا تجاوزت ذلك، يتم أخذ أول رقمين فقط في الاعتبار في التعبير. يسمح باستخدام رموز ASCII في التعبيرات العادية.
\xn
يتوافق مع n، حيث n هي قيمة هروب سداسية عشرية. يجب أن تتكون قيم الهروب السداسية العشرية من رقمين بالضبط. على سبيل المثال، "\x41" يتوافق مع "A". "\x041" يعادل "\x04" و "1". يسمح باستخدام رموز ASCII في التعبيرات العادية.

قد يظهر عنصر في نمط مرة واحدة أو عدة مرات. دعونا نلقي نظرة على بعض الأمثلة التي تتضمن الرمز \d، الذي يمثل رقمًا واحدًا:

النمط
المعنى
\d
رقم
\d?
رقم واحد أو صفر
\d*
رقم واحد أو أكثر
\d+
رقم واحد أو أكثر
\d{2}
رقمان
\d{3,}
3 أرقام على الأقل
\d{5,7}
ما بين 5 و 7 أرقام

والآن، لنتخيل نموذجًا قادرًا على وصف التنسيق المتوقع لسلسلة نصية:

السلسلة المستهدفة
النمط
تاريخ بتنسيق dd/mm/yy
\d{2}/\d{2}/\d{2}
وقت بتنسيق hh:mm:ss
\d{2}:\d{2}:\d{2}
عدد صحيح غير موقّع
\d+
سلسلة من المسافات، قد تكون فارغة
\s*
عدد صحيح غير موقّع قد يسبقه أو يتبعه مسافات
\s*\d+\s*
عدد صحيح قد يكون موقّعًا ويسبقه أو يتبعه مسافات
\s*[+|-]?\s*\d+\s*
عدد حقيقي غير موقّع قد يسبقه أو يتبعه مسافات
\s*\d+(.\d*)?\s*
عدد حقيقي قد يكون له إشارة ويسبقه أو يتبعه مسافات
\s*[+|]?\s*\d+(.\d*)?\s*
سلسلة تحتوي على كلمة "just"
\bjuste\b
  

يمكنك تحديد المكان الذي تريد البحث فيه عن النمط في السلسلة:

النمط
المعنى
^النمط
يبدأ النمط السلسلة
النمط$
ينهي النمط السلسلة
^النمط$
يبدأ النمط السلسلة وينهيها
النمط
يتم البحث عن النمط في أي مكان في السلسلة، بدءًا من البداية.
سلسلة البحث
النمط
سلسلة تنتهي بعلامة تعجب
!$
سلسلة تنتهي بنقطة
\.$
سلسلة تبدأ بالتسلسل //
^//
سلسلة تتكون من كلمة واحدة، قد تسبقها أو تتبعها مسافات
^\s*\w+\s*$
سلسلة تتكون من كلمتين، يمكن أن تسبقها أو تليها مسافات
^\s*\w+\s*\w+\s*$
سلسلة تحتوي على كلمة secret
\bsecret\b

يمكن "استخراج" الأنماط الفرعية لنمط ما. وبالتالي، لا يمكننا فقط التحقق من أن سلسلة ما تتطابق مع نمط معين، بل يمكننا أيضًا استخراج العناصر المطابقة للأنماط الفرعية للنمط التي تم وضعها بين قوسين من تلك السلسلة. على سبيل المثال، إذا كنا نقوم بتحليل سلسلة تحتوي على تاريخ بتنسيق dd/mm/yy ونريد استخراج مكونات dd و mm و yy من ذلك التاريخ، فسنستخدم النمط (\d\d)/(\d\d)/(\d\d).

دعونا نلقي نظرة على هذا المثال لنرى كيفية القيام بذلك باستخدام VBScript.

  • أولاً، نحتاج إلى إنشاء كائن RegExp (التعبير العادي)
set modele=new regexp
  • ثم نحدد النمط المراد اختباره
modele.pattern="(\d\d)/(\d\d)/(\d\d)"
  • قد ترغب في تجاهل حالة الأحرف (بشكل افتراضي، يتم التمييز بين الأحرف الكبيرة والصغيرة). هنا، لا يهم ذلك.
modele.IgnoreCase=true
  • قد ترغب في البحث عن النمط عدة مرات في السلسلة (بشكل افتراضي، لا يتم ذلك)
modele.Global=true

لا يكون البحث الشامل منطقيًا إلا إذا كان النمط المستخدم لا يشير إلى بداية السلسلة أو نهايتها.

  • ثم نبحث عن جميع التطابقات للنمط في السلسلة:
set correspondances=modele.execute(chaine)

تُرجع طريقة execute الخاصة بكائن RegExp مجموعة من كائنات match. يحتوي هذا الكائن على خاصية value تمثل السلسلة التي تتطابق مع النمط. إذا قمت بتعيين pattern.global=true، فقد تكون هناك عدة نتائج مطابقة. ولهذا السبب تكون نتيجة طريقة execute عبارة عن مجموعة من النتائج المطابقة.

  • يتم تحديد عدد المطابقات بواسطة matches.count. إذا كان هذا الرقم 0، فهذا يعني أنه لم يتم العثور على النمط في أي مكان. يتم تحديد قيمة المطابقة #i بواسطة matches(i).value. إذا كان النمط يحتوي على أنماط فرعية بين قوسين، فإن عنصر matches(i) المطابق للقوس j في النمط هو matches(i).submatches(j).

يتم عرض كل هذا في المثال التالي:

البرنامج

' regular expression

' we want to check that a string contains a date in dd/mm/yy format

Option Explicit
Dim modele

' we define the
Set modele=new regexp
modele.pattern="\b(\d\d)/(\d\d)/(\d\d)\b"  ' a date anywhere in the chain
modele.global=true                      ' we will search for the model several times in the chain

' the user specifies the string in which to search for the model
Dim chaine, correspondances, i

chaine=""
' loop as long as string<>"end"
Do While true
    ' the user is asked to type a text
    wscript.stdout.writeLine "Tapez un texte contenant des dates au format jj/mm/aa et fin pour arrêter : "
    chaine=wscript.stdin.readLine
    ' fini si chaine=fin
    If chaine="fin" Then Exit Do
    ' the string read is compared with the date template
    Set correspondances=modele.execute(chaine)
    ' was a match found
    If correspondances.count<>0 Then
        ' we have at least one match
        For i=0 To correspondances.count-1
            ' the i correspondence is displayed
            wscript.echo "J'ai trouvé la date " & correspondances(i).value
            ' we retrieve the sub-elements of correspondence 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
        ' no correspondence
        wscript.echo "Je n'ai pas trouvé de date au format jj/mm/aa dans votre texte"
    End If
Loop

' finish
wscript.quit 0

النتائج

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

باستخدام التعبيرات العادية، يمكن كتابة البرنامج الذي يتحقق مما إذا كانت المدخلات من لوحة المفاتيح هي بالفعل عدد صحيح موجب على النحو التالي:

البرنامج

' read data until it is recognized as a number

Option Explicit

Dim fini, nombre

' we define the model of a positive integer (which may be zero)
Dim modele
Set modele=new regexp
modele.pattern="^\s*\d+\s*$"

' loop until the data entered is correct
' the loop is controlled by a finite boolean, set to false at the start (= it's not finite)

fini=false
Do While Not fini
    ' we ask for the number
    wscript.stdout.write "Tapez un nombre entier >0: "
    ' we read it
    nombre=wscript.stdin.readLine
    ' test the format of the data read
    Dim correspondances
    Set correspondances=modele.execute(nombre)
    ' has the model been checked?
    If correspondances.count<>0 Then
        ' it's an integer, but is it >0?
        nombre=cint(nombre)
        If nombre>0 Then
            fini=true
        End If
    End If
    ' possible error msg
    If Not fini Then wscript.echo "Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp..."
Loop

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

' and end
wscript.quit 0

النتائج

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

قد يمثل العثور على التعبير النمطي الذي يسمح لنا بالتحقق مما إذا كانت سلسلة ما تتطابق مع نمط معين تحديًا حقيقيًا في بعض الأحيان. يتيح لك البرنامج التالي التدرب على ذلك. يطلب البرنامج نمطًا وسلسلة، ثم يشير إلى ما إذا كانت السلسلة تتطابق مع النمط أم لا.

البرنامج

' regular expression

' we want to check that a chain corresponds to a model

Option Explicit

' we define the
Dim modele
Set modele=new regexp
modele.global=true                      ' we will search for the model several times in the chain

' the user specifies the string in which to search for the model
Dim chaine, correspondances, i

Do While true
    ' the user is asked to type in a template
    wscript.stdout.write "Tapez le modèle à tester et fin pour arrêter : "
    modele.pattern=wscript.stdin.readLine
    ' finished?
    If modele.pattern="fin" Then Exit Do
        ' the user is asked for the strings to be compared with the model
        Do While true
            ' the user is asked to type in a template
            wscript.stdout.writeLine "Tapez la chaîne à tester avec le modèle [" & modele.pattern & "] et fin pour arrêter : "
            chaine=wscript.stdin.readLine
            ' finished?
            If chaine="fin" Then Exit Do
            ' the string read is compared with the date template
            Set correspondances=modele.execute(chaine)
            ' was a match found
            If correspondances.count<>0 Then
                ' we have at least one match
                For i=0 To correspondances.count-1
                    ' the i correspondence is displayed
                    wscript.echo "J'ai trouvé la correspondance " & correspondances(i).value
                Next
            Else
                ' no correspondence
                wscript.echo "Je n'ai pas trouvé de correspondance"
            End If
    Loop
Loop

' finish
wscript.quit 0

النتائج

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. معالجة أخطاء وقت التشغيل

هناك طريقة أخرى للتعامل مع أخطاء وقت التشغيل وهي السماح بحدوثها، وتلقي إشعار بها، ثم التعامل معها. عادةً، عند حدوث خطأ في وقت التشغيل، يعرض WSH رسالة خطأ ويتوقف البرنامج. هناك جملتان تسمحان لنا بتغيير هذا السلوك:

on error resume next

يخبر هذا البيان النظام (WSH) بأننا سنتعامل مع الأخطاء بأنفسنا. بعد هذا البيان، يتجاهل النظام أي أخطاء ببساطة.

on error goto 0

يعيدنا هذا البيان إلى معالجة الأخطاء العادية.

عندما يكون البيان on error resume next نشطًا، يجب علينا معالجة أي أخطاء قد تحدث بأنفسنا. يساعدنا الكائن Err في القيام بذلك. يحتوي هذا الكائن على خصائص وأساليب متنوعة، وسنركز على الاثنين التاليين منها:

  • number: عدد صحيح يمثل رقم آخر خطأ حدث. 0 تعني "لا يوجد خطأ"
  • description: رسالة الخطأ التي كان النظام سيعرضها لو لم نصدر عبارة on error resume next

لنلقِ نظرة على المثال التالي:

البرنامج


' unhandled error
 
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'

الآن دعونا نتعامل مع الخطأ:

البرنامج

' managed error

Option Explicit
Dim nombre

' we manage mistakes ourselves
On Error Resume Next
nombre=cdbl("abcd")
' was there a mistake?
If Err.number<>0 Then
    wscript.echo "L'erreur [" & err.description & "] s'est produite"
    On Error GoTo 0
    wscript.quit 1
End If
' no error - returns to normal operation
On Error GoTo 0
wscript.echo "nombre=" & nombre
wscript.quit 0

النتائج

L'erreur [Type incompatible] s'est produite

دعونا نعيد كتابة البرنامج لإدخال عدد صحيح >0 باستخدام هذه الطريقة الجديدة:

البرنامج


' read data until it is recognized as a number
 
Option Explicit
 
Dim fini, nombre
 
' loop until the data entered is correct
' the loop is controlled by a finite boolean, set to false at the start (= it's not finite)
 
fini=false
Do While Not fini
  ' we ask for the number
  wscript.stdout.write "Tapez un nombre entier >0: "
  ' we read it
  nombre=wscript.stdin.readLine
  ' test the format of the data read
  On Error Resume Next
  nombre=cdbl(nombre)
  If err.number=0 Then
    ' no error it's a number
    ' returns to normal error handling mode
    On Error GoTo 0
    ' is it an integer >0
    If (nombre-int(nombre))=0 And nombre>0 Then
      fini=true
    End If
  End If
  ' returns to normal error handling mode
  On Error GoTo 0
  ' possible error msg
  If Not fini Then wscript.echo "Erreur, vous n'avez pas tapé un nombre entier >0. Recommencez svp..."
Loop
 
' confirmation
wscript.echo "Merci pour le nombre entier >0 : " & nombre
 
' and end
wscript.quit 0

النتائج

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

تعليقات:

  • هذه الطريقة هي في بعض الأحيان الطريقة الوحيدة التي يمكن استخدامها. في هذه الحالة، لا تنس العودة إلى وضع معالجة الأخطاء العادي بمجرد اكتمال سلسلة التعليمات التي من المحتمل أن تولد الخطأ.

4.4. التطبيق على برنامج حساب الضرائب

سنعود إلى برنامج حساب الضرائب الذي كتبناه سابقًا، هذه المرة للتحقق من صحة الحجج التي تم تمريرها إلى البرنامج:

البرنامج


' calculating a taxpayer's tax liability
' the program must be called with three parameters: married children salary
' married: character Y if married, N if unmarried
' children: number of children
' salary: annual salary without cents
 
' no verification of data validity is performed, but we do
' check that there are three of them
 
' mandatory variable declaration
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)"
 
' we check that there are 3 arguments
  Dim nbArguments
  nbArguments=wscript.arguments.count
  If nbArguments<>3 Then
    ' error msg
    wscript.echo syntaxe & vbCRLF & vbCRLF & "erreur : nombre d'arguments incorrect"
    ' stop with error code 1
    wscript.quit 1
  End If

' retrieve arguments and check their validity
' an argument is passed to the program without spaces in front and behind it
' use regular expressions to check data validity
  Dim modele, correspondances
  Set modele=new regexp

  ' marital status must be among the characters oOnN
  modele.pattern="^[oOnN]$"
  Set correspondances=modele.execute(wscript.arguments(0))
  If correspondances.count=0 Then
    ' error
    wscript.echo syntaxe & vbCRLF & vbCRLF & "erreur : argument marie incorrect"
    ' we leave
    wscript.quit 2
  End If
  ' the value
  Dim marie
  If lcase(wscript.arguments(0)) = "o"Then
    marie=true
  Else
    marie=false
  End If

  ' children must be an integer >=0
  modele.pattern="^\d{1,2}$"
  Set correspondances=modele.execute(wscript.arguments(1))
  If correspondances.count=0 Then
    ' error
    wscript.echo syntaxe & vbCRLF & vbCRLF & "erreur : argument enfants incorrect"
    ' we leave
    wscript.quit 3
  End If
  ' the value
  Dim enfants
  enfants=cint(wscript.arguments(1))
 
  ' salary must be an integer >=0
  modele.pattern="^\d{1,9}$"
  Set correspondances=modele.execute(wscript.arguments(2))
  If correspondances.count=0 Then
    ' error
    wscript.echo syntaxe & vbCRLF & vbCRLF & "erreur : argument salaire incorrect"
    ' we leave
    wscript.quit 4
  End If
  ' the value
  Dim salaire
  salaire=clng(wscript.arguments(2))

  ' we define the data needed to calculate the tax in 3 tables
  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)

  ' the number of shares is calculated
  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

  ' calculate the family quota and taxable income
  Dim revenu, qf
  revenu=0.72*salaire
  qf=revenu/nbParts

  ' tax calculation
  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))

  ' the result is displayed
  wscript.echo "impôt=" & impot

  ' leave without error
  wscript.quit 0

النتائج

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