Store and Recall Commands

Purpose

Fast save and load of text files.

Syntax

Store #n, a$() [,m]

Recall #n, a$(),m,j

n:integer expression; channel number
a$():one dimensional string array
m:iexp
j:ivar

Description

Store saves the complete string array through the opened channel n (from 0 to 511) to a file. The individual strings in the file saved with Store are separated by a CR/LF. The parameter m is optional and defines how many strings from a$() should be written to the text file.

Recall #n,a$(),m,j reads through an already opened channel n (from 0 to 511) m lines from a text file, into the string array a$(). If m is greater than the number of elements in the string array, the number of reads is automatically limited (m=-1 fills the whole array). If during reading an EOF is reached the reading is stopped without reporting an error. At the end of the read the variable j contains the number of strings actually read in.

Recall expects that the single character strings are separated by CR/LF within the text file. If the text file follows this structure, Recall also can be applied to files which haven't been produced with Store. It should also be noted that, although Store can save string elements of any legal length, Recall can only retrieve upto 9999 characters - see below.

Example

' Create a text file

Local a$(6999), i%, x%

Local fn$ = App.Path + "\Test.txt"

For i% = LBound(a$()) To UBound(a$())

a$(i%) = "Hello world" & Str(i% + 1)

Next i%

Open fn$ for Output As # 1

Store # 1, a$(), UBound(a$()) + 1

Close # 1

' Load text file

' Erase a$() : Dim a$(10000) // This causes an error as Recall can not recognise a$, so use the following...

ReDim a$(10000)

Try

Open fn$ for Input As # 1

Recall # 1, a$(), -1, x%

Close # 1

Message "Lines read in by Recall: " & Str( x%)

OpenW 1

Ocx ListBox lb1 = , 50, 50, _X - 100, _Y - 100

lb1.Sorted = False

For i% = LBound(a$()) To UBound(a$())

If a$(i%) <> "" Then lb1.AddItem a$(i%)

Next i%

Do

Sleep

Until Me Is Nothing

Catch

Message Err.Description

EndCatch

Kill App.Path + "\Test.txt"

Reads the complete text file TEST.TXT in the local folder into the string array a$() and, when finished, prints how many strings have been read in and then loads all elements into a listbox.

Remarks

Store and Recall are for strings what BSave/BLoad or BPut/Bget are for general arrays and memory areas.

Known Issues

1. As shown in the example above, you can not use an array that has been Erase-d in a Recall statement as GFA reads the array as null, does not import any values and returns an error if you try and interrogate the array. Rather than Erase, use Redim instead.


2. As explained in the Description, Recall can only retrieve 9999 bytes for each string element; the remainder of the bytes (or the next 9999) it places in the next string element, nudging all the other elements up one. This is illustrated by the code below:

Local a$(100), n As Int32

For n = 1 To 100 : a$(n) = String(12000, "A") : Next n

Open App.Path & "\store.tmp" for Output As # 1

Store # 1, a$()

Close # 1

For n = 1 To 100 : a$(n) = "" : Next n

Open App.Path & "\store.tmp" for Input As # 1

Recall # 1, a$(), -1, n

Close # 1

Print Len(a$(1))

Print Len(a$(2))

Kill App.Path & "\store.tmp"

To fix this problem, use the RecallX function included in the example below. Also note the StoreX procedure which, even though it is not used in this example, allows you to use customised element dividers.

// Note: Unlike Recall, RecallX() has an optional parameter which,...

// ...if set, redimensions the string array to fit all requested...

// ...elements if the original string is too small

// Both StoreX() and RecallX() allow for a customised element divider

Option Base 0

Local a$(10), n As Int32

For n = 1 To 10 : a$(n) = String(120000, "A") : Next n

Open App.Path & "\store.tmp" for Output As # 1

Store # 1, a$()

Store # 1, a$()

Close # 1

ReDim a$(5)

Open App.Path & "\store.tmp" for Input As # 1

Print "No of elements: "; RecallX(1, a$(), 5, n)

For n = 1 To UBound(a$()) : Print Len(a$(n)) : Next n

Print "No of elements: "; RecallX(1, a$(), -1, n, True)

For n = 1 To UBound(a$()) : Print Len(a$(n)) : Next n

Close # 1

 

Procedure StoreX(filenumber%, ByRef a$(), Optional ct%= -1, Optional elemdiv As Variant)

// Store works exactly as required

// This replacement is only included for if you wish to change the end markers...

// ...of each string from CRLF to something else to allow Store/Recall...

// ...to work with string elements which contain CRLF markers for line breaks...

// ...or when the array is used to save picture and/or file information which...

// ...may have the pairing #13#10 numerous times within one element.

Dim b$(1), c$, ed$, ob| = LBound(b$()), n%

// ob| is used to adjust for Option Base

ed$ = Iif(IsMissing(elemdiv), #13#10, elemdiv)

If ct% <> -1 : If ob| = 0 Then Inc ct%

Else : ct% = UBound(a$()) - Iif(ob| = 0, 1, 0)

EndIf

For n = ob| To ct%

c$ = a$(n) & ed$ : BPut # 1, V:c$, Len(c$)

Next n

EndProcedure

 

Function  RecallX(filenumber%, ByRef a$(), ct%, ByRef retval%, Optional redim?, Optional elemdiv As Variant)

// Inspired by an example by Roger Cabo

Local b$, bsize%, c$, ed$, ended?, p1%, rec%, redimmed?

ed$ = Iif(IsMissing(elemdiv), #13#10, elemdiv)

// If End of File or file is empty, return everything as it was

If EOF(# filenumber%) Then Return 0

// Set Start Element to match Option Base

Dim t%(1) : rec% = LBound(t%()) : retval% = 1

// Clear First Element of Array

a$(rec%) = ""

// Start Retrieving file date

While Not EOF(# filenumber%) And Not ended?

// Calculate size of block to retrieve

bsize% = Min(64000, LOF(# filenumber) - Loc(# filenumber))

// Retrieve String block

b$ = Input$(bsize%, # filenumber%)

// Search for CRLF...

p1% = InStr(b$, ed$)

// ...and split records when found

While p1% <> 0 And Not ended?

a$(rec%) = a$(rec%) & Left(b$, p1% - 1) : b$ = Mid(b$, p1% + Len(ed$))

// If reached predetermined file limit then end...

If ct% <> -1 And retval% = ct% : ended? = True

Else // ...otherwise increase a$() and carry on

If rec% = UBound(a$()) And redim? : ReDim a$(UBound(a$()) + 100) : redimmed? = True

Inc rec% : Inc retval% : a$(rec) = ""

ElseIf rec% = UBound(a$()) : ended? = True

Else : Inc rec% : Inc retval% : a$(rec) = ""

EndIf

EndIf

p1% = InStr(b$, ed$)

Wend

// If End of File reached, add what remains of b$ to the last a$() record...

If Not ended? : a$(rec%) = a$(rec%) & b$

// ...otherwise, b$ may be part of another data block to move file pointer back

Else : Seek # filenumber%, Loc(# 1) - Len(b$)

EndIf

Wend

If Not ended? Then Dec retval% : Dec rec%

If redimmed? Then ReDim a$(rec%)

Return retval%

EndFunction

See Also

Open

{Created by Sjouke Hamstra; Last updated: 16/07/2015 by James Gaite}