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