QBASIC/Visual Basic: Unterschiede zum C16-BASIC


In diesem Kapitel wollen wir uns mit einer Auswahl von Eigenschaften von QBASIC/Visual Basic beschäftigen, zum Teil Sprachelemente, die gegenüber dem C16-BASIC gewisse Erweiterungen enthalten, zum Teil Befehle zur Ein- und Ausgabe, die es Ihnen erleichtern sollen, schon mal mit QBASIC loszulegen.

Die Elemente von QBAsIC, die ganz neue Konzepte einführen, wie Funktionen, Datentypen und Dateioperationen werden wir nicht in diesem, sondern in den nächsten Kapiteln behandeln.

Sprünge

Da man in QBASIC/Visual Basic auf Zeilennummern verzichten kann, muss man offensichtlich etwas anderes dafür vorgesehen haben. So ist es auch. Man springt nicht mehr zu Zeilennummern, sondern zu Sprungmarken. Das funktioniert genau gleich wie in DOS-Skripten, nur dass die BASIC-Sprungmarken den Doppelpunkt nicht vorne, sondern hinten haben.
CLS
PRINT "Taschenrechner"
PRINT "--------------"
start:
INPUT "Geben Sie zwei Zahlen ein: ", a, b
PRINT "Summe: "; a + b
PRINT "Differenz: "; a - b
PRINT "Produkt: "; a * b
PRINT "Verhältnis: "; a / b
IF (a <> -1) GOTO start
Die Sprungmarken sind ein Riesenvorteil, da "GOTO start" wesentlich aussagekräftiger ist, als "GOTO 1023".

Was wir hier auch gleich noch sehen: Der Befehl zum Bildschirmlöschen heisst hier "CLS", nicht "SCRCLR".

IF-Anweisung

Im einfachen Fall sieht eine IF-Anweisung so aus:
IF  THEN 
Was aber, wenn wir im Fall, dass die Bedingung wahr ist, eine ganze Menge machen wollten? Natürlich können wir die Befehle alle mit Doppelpunkten hinter das THEN packen, aber so richtig toll war das auch nicht:
IF antwort$="j" THEN INPUT "Gib noch was ein:";b$:FOR i=1 TO LEN(b$):IF
mid$(b$,i,1)="?" THEN b$=LEFT$(b$,i-1)+RIGHT$(b$,LEN(b$)-i-1):NEXT I:
PRINT b$:GOTO 1234
....
(Diese Zeile würde ausserdem einen Syntaxfehler verursachen, weil man innerhalb einer Doppelpunktsequenz nicht nocheinmal einen IF-Befehl bringen kann.) Die andere Möglichkeit ist, sich mit vielen GOTO's zu helfen, die die Übersicht aber auch nicht gerade erleichtern:
IF  THEN GOTO marke1
IF  THEN GOTO marke2
...
GOTO marke3
marke2:
...
GOTO marke3
marke1:
...
marke3:
...
Alles klar?

Der IF-Befehl in QBASIC/Visual Basic hat schon den vollen Umfang des If's der strukturierten Programmierung:

IF  THEN 
  ...
ELSEIF 
  ...
ELSE
  ...
END IF
An Stelle von "...", "..." usw. können eine oder mehrere Programmzeilen stehen. Inklusive Schleifen und GOSUBs. So einen ganzen Programmabschnitt, der da steht, wo "normalerweise" nur ein einziger Befehl steht, nennt man einen Block. Das ist was sehr Praktisches.

Das Ganze funktioniert also so: Falls Bedingung1 zutrifft, dann führe den Block aus. Ansonsten(ELSE): Wenn (IF) Bedingung2 zutrifft, dann führe den Block aus. Ansonsten: Führe den Block aus. Und damit man weiss, wo der Block hinter dem ELSE zuende ist, muss man diesen mit einem "END IF" abschliessen.

Man muss nicht immer alle Teile dieser langen IF-Anweisung angeben. Man kann auch das ELSEIF oder ELSE weglassen. Es ginge also auch

IF  THEN
  ...
END IF
Oder eben:
IF  THEN
  ...
ELSE
  ...
END IF
Alle Klarheiten beseitigt? Machen wir noch ein einfaches Übungsbeispiel:
INPUT "Geben Sie ein Zahl ein: ",a
IF a=1 THEN
  PRINT "Das ist das Doppelte: ",a*2
  PRINT "Das ist das Vierfache: ",a*4
ELSEIF a=2 THEN
  PRINT "Das ist das Dreifache: ",a*3
  PRINT "Das ist das Sechsfache: ",a*6
ELSE
  PRINT "Das ist weder 1 noch 2"
END IF
Und noch ein etwas raffinierteres Beispiel: Hier enthält der erste Block hinter dem IF selbst nochmal ein IF mit Block:
CLS
PRINT "Seltsamer Rechner"
PRINT "--------------"
start:
INPUT "Geben Sie eine Zahl ein: ", a
INPUT "Wollen Sie wissen, was die Haelfte ist? (j/n)", o$
IF (o$ = "j) THEN
  b = a / 2
  PRINT "Die Haelfte ist: "; b
ELSE
  PRINT "Wollen Sie wissen, was ein Viertel ist? (j/n)", o$
  IF (o$ = "j") THEN
    c = a / 4
    PRINT "Ein Viertel ist: "; c
  END IF
END IF
GOTO start
Wie Sie sehen, empfiehlt es sich, die Blöcke nach rechts einzurücken. Immer alle Zeilen, die zu einem Block gehören, sind gleichweit eingezogen. Dadurch kann man die Blöcke mit dem Auge leicht erkennen.

Schleifen

Soviel Möglichkeiten zum Schleifenbau wie in QBASIC/Visual Basic kenne ich in keiner anderen Sprache. (Schon ein bisschen zuviel und daher etwas unübersichtlich.) Im C16-BASIC haben wir FOR und WHILE kennengelernt. Die gibt's - mit exakt der gleichen Syntax - auch in QBASIC.

EXIT-Anweisung bei FOR:

Ein wichtiges Extra bei der FOR-Schleife ist allerdings die EXIT-Anweisung. Mit dieser Anweisung bricht man eine FOR-Schleife vorzeitig ab. Dadurch können wir uns auch hier ein GOTO sparen.Variante ohne EXIT:

FOR i=1 TO 100
  INPUT "Geben Sie eine Zahl ein: ",x
  IF x=0 THEN GOTO nachschleife
  PRINT 1/x
NEXT i
nachschleife:
...

Variante mit EXIT:

FOR i=1 TO 100
  INPUT "Geben Sie eine Zahl ein: ",x
  IF x=0 THEN EXIT FOR
  PRINT 1/x
NEXT i
...

WHILE

Aus unerfindlichen Gründen gibt's zwar das EXIT bei FOR-Schleifen, nicht aber bei den häufig gebrauchten WHILE-Schleifen. Das liegt wohl daran, dass die einfache WHILE-Schleife lediglich aus Kompatibilitätsgründen aufgenommen wurde. Ihr eigentlicher "grosser" Nachfolger ist die DO WHILE-LOOP-Schleife.

LOOP

Für LOOP-Schleifen gibt es zwei Möglichkeiten. Ich will die eine mal die WHILE-Möglichkeit und die andere die DO-Möglichkeit nennen: Das sieht dann so aus:

WHILE-Möglichkeit:

DO WHILE 
  ...
LOOP

DO-Möglichkeit:

DO 
  ...
LOOP UNTIL  
Es gibt sogar noch zwei andere Möglichkeiten, aber die erwähne ich hier nicht, weil sie erstens programmiertechnisch keinen Vorteil bieten, also nur quasi ein Duplikat der anderen beiden Möglichkeiten darstellen (man nennt sowas auch: "Redundanz": Die beiden anderen Möglichkeiten sind redundant). Und weil sie zweitens kein Äquivalent in den Profisprachen der Pascal- und C-Familien haben. Hier gibt es jeweils nur drei Schleifentypen: FOR, WHILE und REPEAT UNTIL (bzw. DO WHILE).

Die DO WHILE-LOOP-Möglichkeit ist also zur einfachen WHILE-Schleife identisch, bis auf die Möglichkeit, bei die WHILE-LOOP-Schleife mit einem EXIT DO abzubrechen.

Bei der DO-LOOP UNTIL-Möglichkeit wird zuerst der Anweisungsblock ausgeführt und dann geprüft. Ist die Bedingung wahr, dann wird die Schleife abgebrochen, hier also ein weiterer Unterschied zur WHILE-Schleife, wo eine wahre Bedingung zur Fortsetzung der Schleife führt.

Anwendungsbeispiel:

'Suche-Funktion

INPUT "Geben Sie einen String ein: ", a$
INPUT "Geben Sie ein Wort ein, das im String gesucht werden soll:", such$

alen = LEN(a$)
slen = LEN(such$)
i = 1
found = 0

DO WHILE (NOT (found = 1)) AND (i <= alen - slen + 1)
  found = 1
  FOR j = 1 TO slen
    a1$ = MID$(a$, i + j - 1, 1)
    s1$ = MID$(such$, j, 1)
    IF UCASE$(a1$)<>UCASE$(s1$) THEN found=0
  NEXT j
  IF found = 0 THEN i = i + 1
LOOP

IF (found = 1) THEN
  PRINT "Wort an Stelle "; i; " gefunden"
ELSE
  PRINT "Wort wurde nicht gefunden"
END IF
Wir hätten dieses Beispiel auch mit einer einfachen WHILE-Schleife formulieren können, da wir hier nirgends die Schleife abbrechen müssen.

Übung:

  1. Wie müsste die Suchfunktion aussehen, wenn man die äussere Schleife als FOR-Schleife formuliert?
  2. Wie müsste die Suchfunktion aussehen, wenn man die äussere Schleife als DO-LOOP UNTIL-Schleife formuliert?

Abgesehen von den Schleifen haben sich beim obigen Beispiel zwei weitere Neuerungen eingeschlichen: Das Hochkomma in der ersten Zeile und die Funktion UCASE$(). Das Hochkomma ist eine Alternative zu REM: Bei QBASIC/ Visual Basic empfiehlt sich, nur noch das Hochkomma als Kommentar zu benutzen, da so der Kommentar viel leichter vom eigentlichen Programmtext zu unterscheiden ist.

UCASE heisst "UpperCASE" und wandelt einen String in Grossbuchstaben um. Das Gegenstück dazu ist LCASE$(), welches - unschwer zu erraten - einen String in Kleinbuchstaben umwandelt. Durch die Umwandlung in Grossbuchstaben ist unsere Suchfunktion unabhängig von Gross- und Kleinschreibung. Jetzt wissen Sie, wie die Suchefunktion von Textverarbeitungsprogrammen programmiert ist! Nicht schwer, oder?

SELECT CASE

Ganz nett, in allen "grossen" Programmiersprachen vorhanden, aber nicht unbedingt nötig ist der select-Befehl, ein Spezialfall von IF, gedacht für den Fall, dass nach den einzelnen Werten einer Ganzzahlvariablen verzweigt werden muss. Zunächst der Paradefall ohne SELECT:
ende=0
WHILE (ende=0)
  CLS
  PRINT "Ihr Spiele-Menü:"
  PRINT "1. Vier gewinnt"
  PRINT "2. 17 und 4"
  PRINT "3. Formel 1"
  PRINT "4. Superhirn"
  PRINT "5. Millionärsshow"
  PRINT "6. Ende"
  PRINT
  COLOR 14,0
  INPUT "Ihre Wahl: ",a
  IF     a=1 THEN GOSUB viergewinnt
  ELSEIF a=2 THEN GOSUB 17und4
  ELSEIF a=3 THEN GOSUB formel1
  ELSEIF a=4 THEN GOSUB superhirn
  ELSEIF a=5 THEN GOSUB millionär
  ELSEIF a=6 THEN ende=1
WEND
Den letzten Teil ab INPUT... kann man mit SELECT-Befehl so schreiben:
  INPUT "Ihre Wahl: ",a
  SELECT CASE a
  CASE 1
    GOSUB viergewinnt
  CASE 2
    GOSUB 17und4
  CASE 3
    GOSUB formel1
  CASE 4
    GOSUB superhirn
  CASE 5
    GOSUB millionär
  CASE 6
    ende=1
  END SELECT
Hier bringt das nicht wirklich Vorteile gegenüber IF. Wenn man allerdings sehr viele mögliche Werte berücksichtigen muss, die sich nur auf wenige Entscheidungszweige verteilen, dann kann der SELECT-Befehl einiges an Übersicht bringen. Ein
SELECT CASE a
CASE 1,4,6,10,18
  b=1
CASE 2,3,12,13,17
  b=2
CASE 5,7,8,9,11
  b=3
CASE ELSE
  b=4
END SELECT
ist doch erheblich übersichtlicher als ein
IF a=1 OR a=4 OR a=6 OR a=10 OR a=18 THEN b=1
ELSE IF a=2 OR a=3....
Damit ist die Syntax von SELECT eigentlich auch schon erklärt:
SELECT CASE Variable
CASE Werteliste1
  ...
CASE Werteliste2
  ...
CASE ELSE
  ...
END CASE
...heisst: Wenn "Variable" einen Wert der Werteliste1 annimmt, (Werte durch Komma getrennt), dann führe den Anweisungsblock hinter dem entsprechenden CASE aus. Entsprechend die anderen CASE-Teile. Es kann, muss aber kein CASE ELSE folgen. Dessen Anweisungsblock wird durchlaufen, falls kein Wert einer der Wertelisten "gepasst" hat.

Texteingabe und -ausgabe

Auch hier hat QBASIC anderes oder mehr zu bieten, als das C16-BASIC.
CLS: Bildschirm löschen
Syntax: CLS
LOCATE: Cursor auf dem Bildschirm positionieren
Syntax: LOCATE y,x (Kurzversion. Langversion: Siehe Hilfe).
y=Zeile (1..25), x=Spalte (1..80)
COLOR: Bildschirmfarben setzen
Syntax: COLOR vordergr,hintergr,rahmen.
vordergr,hintergr und rahmen sind Zahlen, die die Farbnummer angeben. Da Sie vermutlich in einem DOS-Fenster unter Windows arbeiten, bleibt "Rahmen" wirkungslos. Weiter unten ein kleines Beispielprogramm zum Ausprobieren der Farben.
Beispiel zu COLOR:
CLS
FOR i = 0 TO 15
  COLOR i, 0
  PRINT "hhhhhhhhhhhhhhhhhh"
  REM INPUT a$
NEXT i
CLS
FOR i = 0 TO 15
  COLOR 15, i
  PRINT "hhhhhhhhhhhhhhhhhh"
  REM INPUT a$
NEXT i
FOR i = 0 TO 15
  COLOR 15, i
  CLS
  INPUT a$
NEXT i
END

Formatierte Ausgabe mit PRINT USING:

Nehmen wir an, Sie wollen ein Programm zur Erstellung von Rechnungen oder Bilanzen erstellen - für einen Computer sicher ein nicht allzu weit entferntes Anwendungsgebiet. Dieses Programm wird ziemlich viele Zahlen ausspucken. Und es wird nicht so gut aussehen, wenn diese in der Form
12398.463723
183262.12
-28322.34762E00
usw.
Für eine Rechnung wünschen wir uns die Zahlen rechtsbündig und auf exakt zwei Nachkommastellen gerundet.

Zunächstmal ist das eine sehr gute

Übung: Schreiben Sie ein Programm, dass die im Array zahlen() gespeicherten Beträge rechtsbündig und auf zwei Nachkommastellen gerundet auf dem Bildschirm ausgibt!

Wenn Sie nicht weitergkommen sein sollten, hier ein Hinweis auf den Lösungsweg:

  1. Vergessen Sie nicht, die Aufgabe in kleine Teilaufgaben zu unterteilen, die Sie in Unterprogrammen lösen.
  2. Jede Zahl muss zuerstmal wie gefragt gerundet werden (erste Teilaufgabe).
  3. Dann muss sie in einen String umgewandelt werden. (Zweite Teilaufgabe).
  4. Dann muss dieser String auf eine feste Zahl von Zeichen verlängert werden. Und zwar von links her. (Dritte Teilaufgabe).
Und schon haben Sie's.

Es ist also nicht so, dass wir das Problem mit C16-BASIC nicht hätten lösen können. Aber so nett das Austüfteln solcher kleinen Lösungen ist - wenn man nur noch damit beschäftigt ist und nicht mehr mit der eigentlich zu lösenden Aufgabe, macht's auch keinen Spass mehr und man kommt nicht mehr vorwärts.

Daher gilt ganz allgemein: Je umfangreicher der Satz an mitgelieferten Routinen (Unterprogrammen, Befehlen, Ressourcen - darunter können Sie vorläufig dasselbe verstehen) eines Entwicklungssystems ist, desto

  1. länger brauchen Sie, bis Sie dieses zu 100% nutzen können.
  2. desto produktiver sind Sie dann aber auch im Endeffekt.

Ein gutes Beispiel einer solchen mitgelieferten Ressource ist der PRINT USING- Befehl. Er benutzt einen Extra-String, mit dem angegeben werden kann, in welchem Format die folgenden Zahlen oder Strings ausgegeben werden sollen, d.h. mit wieviel Stellen vor und nach dem Komma, rechts- oder linksbündig die Zahl angezeigt werden soll. Deshalb heisst dieser Extrastring - nicht nur in QBASIC, auch in der wichtigen Sprache C gibt es so etwas - Formatstring.

Der String gibt zunächst die Länge des Felds an, in dem die Zahl ausgegeben werden soll.
"#####" z.B. gibt jede Zahl in einem fünf Zeichen langen Feld aus und zwar rechtsbündig. (Eine Option für linksbündige Ausgabe gibt es nicht.)

FOR i=80 TO 89
  PRINT USING "##########";i
NEXT i
ergibt dann die Ausgabe
        80
        81
        82
        83
        84
        85
        86
        87
        88
        89
Jedes #-Zeichen steht also für eine Ziffer oder - falls die Zahl nicht so lang ist - für ein führendes Leerzeichen. Mit einem Dezimalpunkt kann angegeben werden, auf wieviel Stellen gerundet werden soll:
"##########.##" rundet auf 2 Stellen und gibt das Ganze rechtsbündig in einem 10+1+2=13 Zeichen langen Feld aus.

Wenn man dem String ein Plus voranstellt, druckt es die Zahl immer mit führenden Vorzeichen:

FOR i=80 TO 89
  PRINT USING "+##########";i
NEXT i
ergibt dann die Ausgabe
        +80
        +81
        +82
        +83
        +84
        +85
        +86
        +87
        +88
        +89

Wenn man es hintenanstellt ("##########+") druckt PRINT USING das Vorzeichen hinter die Zahl.

Auch für Strings kann man die Länge des Ausgabefelds definieren. Hierzu schreibt man einen String mit der Länge des Ausgabefeldes aus Leerzeichen und führendem und abschliessendem Backslash. "\      \" z.B. sind 6 Leerzeichen plus die Backslashes=8 Zeichen langes Ausgabefeld. Ist der String länger, so werden nur die die ersten acht Zeichen davon ausgegeben, allgemein: Nur soviel Zeichen, wie der Formatstring lang ist.

Ganze Syntax: PRINT USING <Formatstring>Wert1;Wert2;;Wert3;....

Erst kommt also das Schlüsselwort PRINT USING, dann der Formatstring, dann ein Semikolon und dann durch Semikolons getrennt alle Werte, die in diesem Format ausgegeben werden sollen. Dann hinter einem weiteren Semikolon ev. ein neuer Formatstring und dahinter die Werte, die mit dieser neuen Formatangabe gedruckt werden sollen usw. Ganz am Ende gilt die Regelung des normalen Print-Befehls: Ein Semikolon setzt die nächste Ausgabeposition unmittelbar ans Ende dieser Ausgabe. Beim Komma geht's das nächste Mal am nächsten Tabulator weiter und wenn weder Komma noch Semikolon kommt, geht's in der nächsten Zeile weiter.

Im Formatstring von PRINT USING gibt's noch weitere Optionen, die aber nicht so interessant sind, zumindest nicht für Nicht-Bewohner der USA, da diese eher USA-spezifisch sind (Ausgabe des Dollarzeichens, zifferntrennenden Kommatas etc.). Wenn Sie neugierig sind: Schauen Sie in der Online-Hilfe von QBASIC die komplette Syntax nach!