DATA VALIDATION AND ERROR TRAPPING
(debugging code BEFORE you run it!)

Home

Data Validation and Error Trapping
Tipcorner - binary file access
Updating the Open Source Editor
Spotlight on Polar Coordinates
polar1.bas by Nally
atari2.bas by Nally
polar coordinate demo

Have another look at the quote by Yogi Berra:

"You've got to be very careful if you don't know where you're going, because you might not get there."

If you want to write computer programs, you need to know where they are going! You must have a clear idea of what you want your program to do. With that idea firmly in mind, you can debug your programs AS YOU WRITE THEM, before you've even given them a sample run!

Proactive debugging requires data validation and error trapping. This just means that you must insure that data used by the program will actually work, and is the kind of data required by your program, and is in the proper range.


DIVIDE BY ZERO?
A common error that crashes programs is an attempt to divide by zero. The answer, if the computer could give us one, is infinity! But the computer cannot do that, and so it is an impossible calculation. An attempt to divide by zero will cause the program to halt with an error. You might find yourself dividing data to compute an average, for instance, or to organize records, figure a score, etc.

Here is a tiny program that asks the user for two numbers, then it attempts to divide the first number by the second number.

If the user entered 0 for the second number, and the program didn't trap that possibility, then it would halt with an error. It was quite simple to test for a value of 0, and to avoid trying the division if it was equal to 0.

'snip 
     'trapping divide by zero error:
     input "Type first number ";number1
     input "Type second number ";number2
if number2=0 then
     print "Cannot divide by zero!"
     end
     else
     print "The result of dividing is ";
     print number1/number2
     end
     end if
     'end snip


VALID NUMBERS?
There are other reasons to validate numeric data. You might ask a user for his age, and he might answer "eighteen". Hmm. Your program would see that as "0", and this could cause some serious problems! Here is a trivial example that checks for a valid age input from a user. It requests further input from the user if a valid age isn't entered.

[again]
     input "Age?";age
if age=0 or age > 110 then
     print "Please try again!"
     goto [again]
     end if
print "Your age is ";age


You could also assign a default value in the event that a piece of data is outside of the acceptable range. Here is another trivial example. This one sets a value for age if it finds an invalid value.

input "Age?";age
if age=0 then age=10
     if age>110 then age=90
     print "Your age is ";age


Have you spotted the flaw in the above samples? Sometimes, you have to put on your thinking cap to imagine the ways that a program can go wrong. What if the user enters a NEGATIVE number? Let's fix that, too:

input "Age?";age
if age<10 then age=10
     if age>110 then age=90
     print "Your age is ";age 

Now, if any age less than 10 is entered, age is set to a default of 10. If a user entered -345, age would be set to a valid number of 10. Of course, your program might need to handle this eventuality differently than the little samples here. The point is to find the flaws so that your program can fix them and prevent corrupted data and even program crashes.


Okay, now that we've established some ways to check for valid numeric input, we'll think about valid text input. The easiest check is for any input at all. In this little demo, the user is asked for his name:

input "Name?";name$
if name$="" then
     print "You didn't enter a name!"
     else
     print "Hello, ";name$
     end if


What if we need to get specific text input from a user? The following demo asks the user to choose red or blue. Have a look:

input "Red or blue?";color$
select case color$
case "Red"
     print "You chose red!"
case "Blue"
     print "You chose blue!"
case else
     print "You didn't choose a valid color!"
end select


VALID TEXT?
What happens if the user types "red", or "RED"? The program prints "You didn't choose a valid color!" This is another trivial example, but you might have a program that relies upon accurate text data. To check for accuracy regardless of case, use UPPER$() or LOWER$() to evaluate the text. In the following modification of the color input demo, the user's answer is changed to lowercase so that it can be evaluated. Now, he can enter "red", "RED", "rEd" or any other variation and the program will know that he choose the color red.

input "Red or blue?";color$
color$=lower$(color$)
select case color$
case "red"
     print "You chose red!"
case "blue"
     print "You chose blue!"
case else
     print "You didn't choose a valid color!"
end select


FILE EXIST?
Another proactive debugging technique requires checking for a file's existence before attempting to open it or use it. If you try to load a bitmap file that doesn't exist, the program will halt with an error. Imagine that you have included some bmpbutton images in your distribution, and your user gets curious or careless and renames or deletes some of these files. When the program hits the LOADBMP or BMPBUTTON command, it will halt with an error.

If a program attempts to open a disk file for input and it doesn't exist, again the program will halt with an error.

If a program attempts to rename a file and the new filename is that of an existing file, the program will halt with an error. For instance:

name "c:\test.txt" as "c:\hello.txt"

If "c:\hello.txt" is the name of an existing file, then the program will halt with an error.

It is easy to test for a file's existence, and this is explained in detail in newsletter #95, so please check there!

INDEX OUT OF BOUNDS?
If you try to access an array element that is larger than the dimension of the array, you'll get an index out of bounds error. To avoid this error, be sure to DIM your arrays to be large enough! If you have no idea at design time how many elements an array will need to contain, then find a way to check the data to get a number, then DIM or REDIM the array to be just a bit larger than that number. Just remember that REDIMming an array will erase the previous contents, so it will need to be filled again.

Here's an example that reads data from an ini file into an array. It first read in data simply to count the number of items. It then DIMS and array to the needed size. The second time through, it reads the items into the array.

open "lbasic3.ini" for input as #f
while NOT(EOF(#f)) 'while end of file not reached
     input #f, item$
     i=i+1 'increment counter
     wend
close #f
DIM ar$(i+2) 'DIM array large enough to hold data
     i=0 'reset counter
open "lbasic3.ini" for input as #f
     while NOT(EOF(#f))
     input #f, ar$(i) 'input data to array
     i=i+1
     wend
close #f
for j=1 to i
     print ar$(j) 'print it out so we can check it
     next
     end

API ERROR?
Most API function calls have a return value. Sometimes the return value has meaning. It may be that a return of nonzero equal success. It may be that a return of zero equals success! The return may also be a value that will be used by your program. Whatever the return is, it is always a good idea to check its validity before attempting to continue. If the return shows an error, then your program should trap it, as in this immitation sample:

calldll #mydll, "DoAFunction",arg as long, result as long
     if result<>0 then
     notice "Error! Please try again!"
     wait
     end if

FILES, WINDOWS, DLLS NOT CLOSED?
If a program exits without closing all windows, files and DLLS that it has opened, then the program may continue to reside in memory, causing problems, and perhaps a computer crash. You may also see the program itself crash. To be assured that this doesn't happen, be sure to issue a trapclose command that references a branch label where all files, windows and DLLS are closed before an END statement is issued. Don't forget to end all programs with an END statement! Remember, you MAY have a menu item, button, etc. that allows the user to close the program, and that goes to the [quit] branch label, but the user might close the program using the system menu, the X in the corner of the titlebar, or by hitting ALT-F4. Those are the "closes" that you'll need to trap with a trapclose command. Example:

nomainwin
     open "A Window" for window as #1
     print #1, "trapclose [quit]"
     wait
[quit]
     close #1:end


Home

Data Validation and Error Trapping
Tipcorner - binary file access
Updating the Open Source Editor
Spotlight on Polar Coordinates
polar1.bas by Nally
atari2.bas by Nally
polar coordinate demo