Schnelle Dateien

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           #
' ################################# 
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    SPEICHERN Laden"
    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 1=0 
    Do
        Read Wahl$(I)
        Exit If Wahl$(I)="----"
        Inc I 
    Loop
    Wahl$(1)=""
    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);"Bitte Geduld"
    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,"Ok",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, "Ok",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 erstell werden? ";Ra$r 
    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


Franz Schott


Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]