Wer sich mit der Programmierung von Dateien beschĂ€ftigt, sollte die verschiedenen Lade-und Speicherzeiten berĂŒcksichtigen, die in den Tabellen in Bild 1 und2 angegeben sind. Nutzen wir bei der Dateiverwaltung erstens die Schnelligkeit unseres ST und zweitens die des GFA-BASIC.
Wie aus der Tabelle in Bild 2 ersichtlich, werden zum Laden einer sequenziellen Datei mit Input# 104,4 Sekunden benötigt, wĂ€hrend dieselben DatensĂ€tze mit dem durch >Block-R< gekennzeichneten Verfahren schon nach 5.71 Sekunden zur VerfĂŒgung stehen.
Es lohnt sich also, sich bereits bei der Planung eines Programmes hierĂŒber einige Gedanken zu machen.
Die Tabellen in den Bildern 1 und 2 sind Hardcopies von dem in Listing 1 gezeigten Testprogramm (siehe nĂ€chste Seite). Bei Benutzung einer RAM-Disk ergeben sich weitere Vorteile, die bei einer Hard-Disk Ă€hnlich liegen dĂŒrfte (ich konnte dies leider nicht testen).
Nun zu einzelnen Varianten: Gespeichert wurden 375 DatensĂ€tze zu je 75 Zeichen. Die unterschiedliche LĂ€nge der Dateien resultiert aus dem beim PRINT#-Befehl angehĂ€ngten Carriage Return (13) und Line Feedt (10), das sind je Datensatz 12 Bytes. PRINT# und INPUT# sind sehr zeitaufwendig und sollten vor allem bei gröĂeren Dateien nicht benutzt werden. Schneller geht es mit den Random-Access-Dateien, deren einzelne Felder und damit die DatensĂ€tze gleiche LĂ€ngen haben - in unserem Beispiel 6 Felder mit 15, 10,20, 8,12 und 10 Zeichen (gesamt 75). Ein weiterer Vorteil dieser Dateiart mit PUT# und GET# ist die Zugriffsmöglichkeit auf einzelne DatensĂ€tze in der Datei.
Es geht aber Dank der leistungsfĂ€higen GFA-Befehle noch schneller. Die gesamte Datei wird als Block von Diskette in einen String geladen bzw. aus einem String als Block gespeichert. Vor dem Laden muĂ natĂŒrlich ein String mit der erforderlichen LĂ€nge zur Aufnahme bereitstehen. Von diesem String (Dat$ im Listing) aus werden dann die einzelnen DatensĂ€tze DS(374,5) aufbereitet. Beim Speichern werden die einzelnen DatensĂ€tze vor dem Speichern in den String aufgenommen. Das hört sich alles ein biĂchen umstĂ€ndlich an, aber es ist die schnellste Methode, Die DatensĂ€tze können nach Herzenslust verĂ€ndert, gelöscht und angefĂŒgt werden und sind dann schnell wieder abgespeichert. Man hĂŒte sich jedoch davor, den String nach dem Motto 'Dat$=Dat$+D$(x,y)â aufzubauen. Beim Ausprobieren bitte Kaffeepause einlegen. Im Testprogramm werden hierfĂŒr BMOVE und Mid$ verwendet, in den Tabellen 1 und 2 ist diese Variante mit Bsave und Block-R gekennzeichnet.
Bleibt noch das Verfahren Block-I zu erklĂ€ren. Es ist anzuwenden zum Laden von Dateien, die mit PRINT# abgespeichert sind. Die Datei wird zunĂ€chst ebenfalls als Block in einen vorbereiteten String geladen, dann aber wegen der unterschiedlichen DatenlĂ€ngen Byte fĂŒr Byte nach den abschlieĂenden Carriage Returns abgesucht und so die einzelnen Datenfelder herausgefischt. GegenĂŒber dem INPUT#-Befehl ergibt sich eine Zeitersparnis von ca. 60%, es können alle sequenziellen Dateien damit eingelesen werden und evtl, nach vorheriger Aufbereitung auf einheitliche LĂ€ngen mit Bsave wieder abgespeichert werden.
Nachdem nun die VorzĂŒge einer solchen Block-Datei aufgezeigt wurden, soll auch der Nachteil nicht verschwiegen werden. Die in den Tabellen dargestellte DateilĂ€nge hat fast die maximale GröĂe von 32767 Bytes erreicht. GröĂer kann ein String nicht definiert werden (oder vielleicht doch mit dem neuen GFA V 3.0?).
Aber lassen wir den Kopf nicht hÀngen, unser GFA-BASIC hÀlt ja noch weitere sehr gute Befehle bereit.
Wir RESERVEieren uns einfach den benötigten Speicherbereich und verwenden statt Varptr(Dat$) = H1MEM.
Ein Test mit 2000 DatensÀtzen (150000 Bytes) ergab:
Speichern mit PUT# 179.17 Sekunden
Bsave (Himem) 48.25 Sekunden
Laden mit GET# 65.48 Sekunden
Bload (Himem) 35.06 Sekunden
Beim Reservieren von Speicher sollte man darauf achten, daĂ nicht mehr als notwendig reserviert wird, andererseits die Datei aber auch in den reservierten Bereich hineinpaĂt. Um beim Testen nicht bei jedem Start neuen Speicherplatz zu reservieren, kann der RESERVE-Befehl in eine If-Abfrage verpackt werden z.B. beim ST 1040:
If Himem >700000
Reserve Fre(0)-300000
Endif
Nun noch einige ErklĂ€rungen zum Listing: Das Testprogramm besteht im Wesentlichen aus der Initialisierungsroutine mit dem Aufbau der MenĂŒleiste und der Tabellengrafik sowie der Dimensionierung der Variablen, der Hauptschleife und den einzelnen Prozeduren.
Bild 1: Speicherung auf RAM-DISK
Die benutzerfreundliche und einfach zu programmierende MenĂŒleiste dĂŒrfte auch fĂŒr den Einsteiger keine Probleme aufwerfen. Nach der Initialisierung wartet der ST in der nur 3 Zeilen groĂen Warteschleife auf einen MenĂŒaufruf, landet dann in der Prozedur Auswahl, sucht sich hier den angeklickten MenĂŒpunkt, arbeitet dann das entsprechende Unterprogramm ab und begibt sich, nachdem die MenĂŒleiste aufgebaut ist, wieder zur Warteschleife.
Die einzelnen Prozeduren fĂŒr das Laden und Speichern der Dateien bedĂŒrfen keiner besonderen ErklĂ€rung, die besonderen Funktionen wurden bereits erlĂ€utert. In Prozedur Eil werden Dateiname (Fi$) und LĂ€nge der Datei (L%, falls schon vorhanden) ermittelt. Die Prozedur Zeit errechnet den benötigten Zeitaufwand einschlieĂlich Datenaufbereitung und gibt diese in einer Tabelle auf den Bildschirm aus. Mit der Prozedur Ausgabe werden die DatensĂ€tze auf dem Bildschirm ausgegeben, damit auch die letzten Zweifel auf das sichere Funktionieren beseitigt werden. Die DatensĂ€tze können mit der Maus vorwĂ€rts und rĂŒckwĂ€rts gescrollt werden. Durch DrĂŒcken beider Maustasten gelangt man zurĂŒck zur Warteschleife. Die umstĂ€ndliche AusfĂŒhrung der Print-Befehle rĂŒhrt von der ungĂŒnstigen LĂ€nge der DatensĂ€tze her. Die bereits festgelegten FeldlĂ€ngen wollte ich nicht mehr Ă€ndern und die einzelnen Felder trotzdem mit einem Leerzeichen listen. SchlieĂlich werden in der Prozedur Anlegen die Test-DatensĂ€tze erzeugt.
Es ist noch anzumerken, daĂ mit RĂŒcksicht auf die LĂ€nge dieses Testprogrammes keine vollstĂ€ndige Fehlerbehandlung integriert ist. Diese darf in einem Anwendungsprogramm jedoch keineswegs fehlen. So muĂ z.B. das Programm abgefangen werden, wenn versucht wird, eine Datei abzuspeichem. ohne daĂ sich eine solche im Speicher befindet oder die Fehlermeldung nach Gosub Fil bei Anklicken von Abbruch und ok mit Leerstring (nur Len<FiS)>2 erlauben).
Diese Fehler können bei den einzelnen Prozeduren lokalisiert und bequem in eine gemeinsame Stringvariable verpackt werden, die dann am Ende der Prozedur Auswahl auf den Bildschirm ausgegeben werden kann und so auf unterlassene Bedienungshandlungen aufmerksam macht.
Bild 2: Speicherung auf Diskette
' #################################
' # Schnelle Dateien in GfA-Basic #
' # Schott Franz #
' # Bahnhofsplatz 3 #
' # 8425 Neustadt/Donau #
' # (c) MAXON Computer GmbH #
' #################################
GOSUB i.nit
DO
ON MENU
LOOP
PROCEDURE auswahl
MENU OFF
m0%=MENU(0)
CLS
IF wahl$(m0%)=" erstellen "
GOSUB anlegen
ENDIF
IF wahl$(m0%)=" Datei "
GOSUB ausgabe
ENDIF
IF wahl$(m0%)=" Zeittabelle "
SPUT bild$
REPEAT
UNTIL MOUSEK OR INKEY$<>""
ENDIF
IF wahl$(m0%)=" Print# "
GOSUB s.ave_print
ENDIF
IF wahl$(m0%)=" Bsave "
GOSUB s.ave_bsave
ENDIF
IF wahl$(m0%)=" Put# "
GOSUB s.ave_put
ENDIF
IF wahl$(m0%)=" Get# "
GOSUB l.oad_get
ENDIF
IF wahl$(m0%)=" Input# "
GOSUB l.oad_input
ENDIF
IF wahl$(m0%)=" Block-R "
GOSUB l.oad_block_r
ENDIF
IF wahl$(m0%)=" Block-I "
GOSUB l.oad_block_i
ENDIF
IF wahl$(m0%)=" Ende "
EDIT
ENDIF
CLS
MENU wahl$()
RETURN
PROCEDURE i.nit
CLS
re$=CHR$(27)+"p"
ra$=CHR$(27)+"q"
PRINT AT(7,5);re$'
PRINT "Lade- und Speicherzeiten von Dateien mit GfA-Basic in Sekunden ";ra$
PRINT
PRINT '"LĂ€nge S P E I C H E R N L a d e n "
PRINT '"Byte Print# Put# Bsave Input# Get# Block-R ";
PRINT "Block-I Datei"
LINE 0,50,639,50
LINE 0,134,639,134
LINE 0,94,639,94
LINE 64,94,64,300
FOR n=132 TO 630 STEP 72
LINE n,112,n,300
NEXT n
LINE 276,94,276,300
LINE 564,94,564,300
SGET bild$
CLS
DIM d$(2500,5)
DIM wahl$(35)
DIM f$(5)
RESTORE m.datas
i=0
DO
READ wahl$(i)
EXIT IF wahl$(i)="~~~"
INC i
LOOP
wahl$(i)=""
m.datas:
DATA DESKTOP, Information,--------------------
DATA , , , , , ,""
DATA DATENSĂTZE, erstellen ,""
DATA LADEN, Input# , Get# , Block-R , Block-I ,""
DATA SPEICHERN, Print# , Put# , Bsave ,""
DATA AUSGABE, Datei , Zeittabelle ,""
DATA QUIT, Ende ,""
DATA ~~~,""
ON MENU GOSUB auswahl
ON BREAK GOSUB ende
MENU wahl$()
RETURN
PROCEDURE l.oad_get
GOSUB fil
t=TIMER
OPEN "R",#1,fi$,75
FIELD #1,15 AS d0$,10 AS d1$,20 AS d2$,8 AS d3$,12 AS d4$,10 AS d5$
FOR n%=1 TO l%/75
GET #1,n%
d$(n%-1,0)=d0$
d$(n%-1,1)=d1$
d$(n%-1,2)=d2$
d$(n%-1,3)=d3$
d$(n%-1,4)=d4$
d$(n%-1,5)=d5$
NEXT n%
z%=n%-1
CLOSE #1
t1=TIMER
sp%=47
GOSUB zeit
RETURN
PROCEDURE l.oad_block_r
GOSUB fil
IF l%<=32767
t=TIMER
dat$=SPACE$(l%)
BLOAD fi$,VARPTR(dat$)
z%=0
pa%=1
feld:
DATA 15,10,20,8,12,10
REPEAT
RESTORE feld
FOR r%=0 TO 5
READ f%
d$(z%,r%)=MID$(dat$,pa%,f%)
ADD pa%,f%
NEXT r%
INC z%
UNTIL pa%=>l%
t1=TIMER
sp%=56
GOSUB zeit
ELSE
ALERT 3,"Datei ist zu groĂ.|Bitte Himem verwenden!",1,"klar",a%
ENDIF
RETURN
PROCEDURE l.oad_block_i
GOSUB fil
IF l%<32767
t=TIMER
dat$=SPACE$(l%)
BLOAD fi$,VARPTR(dat$)
z%=0
pa%=0
DO
FOR r%=0 TO 5
pe%=0
REPEAT
INC pe%
UNTIL MID$(dat$,pa%+pe%,1)=CHR$(13) OR pe%>500
d$(z%,r%)=SPACE$(pe%-1)
BMOVE VARPTR(dat$)+pa%,VARPTR(d$(z%,r%)),pe%-1
ADD pa%,pe%+1
NEXT r%
INC z%
EXIT IF pa%=>l% OR pe%>500
LOOP
t1=TIMER
sp%=65
GOSUB zeit
ENDIF
RETURN
PROCEDURE l.oad_input
l%=0
GOSUB fil
PRINT AT(26,12);"B i t t e G e d u l d"
t=TIMER
IF l%
z%=0
OPEN "I",#1,fi$
REPEAT
FOR r%=0 TO 5
INPUT #1,d$(z%,r%)
EXIT IF EOF(#1)=-1
NEXT r%
INC z%
UNTIL EOF(#1)=-1
CLOSE #1
t1=TIMER
sp%=38
GOSUB zeit
ENDIF
RETURN
PROCEDURE s.ave_put
GOSUB fil
l%=z%*75
t=TIMER
OPEN "R",#1,fi$,75
FIELD #1,15 AS d0$,10 AS d1$,20 AS d2$,8 AS d3$,12 AS d4$,10 AS d5$
FOR n%=0 TO z%-1
d0$=d$(n%,0)
d1$=d$(n%,1)
d2$=d$(n%,2)
d3$=d$(n%,3)
d4$=d$(n%,4)
d5$=d$(n%,5)
PUT #1,n%+1
NEXT n%
CLOSE #1
t1=TIMER
sp%=20
GOSUB zeit
RETURN
PROCEDURE s.ave_print
GOSUB fil
l%=z%*87
IF LEN(fi$)>2
t=TIMER
OPEN "O",#1,fi$
FOR n%=0 TO z%-1
FOR r%=0 TO 5
PRINT #1,d$(n%,r%)
NEXT r%
NEXT n%
CLOSE #1
t1=TIMER
sp%=11
GOSUB zeit
ELSE
ALERT 2,"Keine gĂŒltiger Dateiname",1,"0k",a%
ENDIF
RETURN
PROCEDURE s.ave_bsave
IF z%<436
GOSUB fil
l%=z%*75
IF LEN(fi$)>2
t=TIMER
pa%=0
dat$=SPACE$(l%)
FOR n%=0 TO z%-1
RESTORE feld
FOR r%=0 TO 5
READ pe%
BMOVE VARPTR(d$(n%,r%)),VARPTR(dat$)+pa%,pe%
ADD pa%,pe%
NEXT r%
NEXT n%
BSAVE fi$,VARPTR(dat$),l%
t1=TIMER
sp%=29
GOSUB zeit
ELSE
ALERT 2,"Keine gĂŒltiger Dateiname",1,"0k",a%
ENDIF
ELSE
a$="Datei ist mit "+STR$(z%*75)+" Bytes|zu groĂ fĂŒr den reservierten|Speicher"
ALERT 3,a$,1,"ok",a%
ENDIF
RETURN
PROCEDURE fil
FILESELECT "\*.*","",fi$
IF EXIST(fi$)
OPEN "I",#1,fi$
l%=LOF(#1)
CLOSE #1
ENDIF
RETURN
PROCEDURE zeit
sek=(t1-t)/200
n%=0
ze%=0
REPEAT
INC n%
IF f$(n%)=""
f$(n%)=fi$
ze%=9+n%
ENDIF
IF f$(n%)=fi$
ze%=n%+9
ENDIF
UNTIL ze%>8
SPUT bild$
PRINT AT(sp%,ze%);
PRINT USING "###.##",sek
PRINT AT(1,ze%);
PRINT USING "######",l%
PRINT AT(72,ze%);fi$
PRINT AT(30,24);re$;" T A S T E ";ra$'
SGET bild$
REPEAT
IF INKEY$="1"
BSAVE "zeittest",VARPTR(bild$),16000
ENDIF
UNTIL INKEY$="a" OR MOUSEK
RETURN
PROCEDURE ausgabe
n%=-1
REPEAT
INC n%
FOR r%=0 TO 5
PRINT d$(n%,r%)'
NEXT r%
PRINT CHR$(27);"A"
UNTIL n%=23
DO
MOUSE x,y,k
IF k=1 AND n%<z%-1
INC n%
PRINT AT(1,25);
FOR r%=0 TO 5
PRINT d$(n%,r%)'
NEXT r%
PRINT CHR$(27);"A"
ENDIF
IF k=2 AND n%>23
BMOVE XBIOS(2),XBIOS(2)+1280,30720
DEC n%
PRINT AT(1,1);
FOR r%=0 TO 5
PRINT d$(n%-23,r%);CHR$(0+(32 AND r%<5));
NEXT r%
ENDIF
EXIT IF k=3
LOOP
RETURN
PROCEDURE anlegen
PRINT
PRINT 're$;" Wieviele DatensÀtze sollen erstellt werden? ";ra$'
INPUT " ",z%
l%=(z%)*75
PRINT AT(30,12);"Erstelle Datensatz Nr. "
FOR n%=0 TO z%-1
PRINT AT(53,12);n%'
d$(n%,0)="Datensatz "+RIGHT$("...."+STR$(n%),5)
d$(n%,1)=STRING$(10,RANDOM(26)+65)
d$(n%,2)=">20-Byte-Datenfeld< "
d$(n%,3)=STRING$(8,RANDOM(10)+48)
d$(n%,4)=LEFT$(STRING$(6,STR$(n%)+"-"),12)
d$(n%,5)="SchluĂfeld"
NEXT n%
RETURN