Fast save and load of text files.
Store #n, a$() [,m]
Recall #n, a$(),m,j
n:integer expression; channel number
a$():one dimensional string array
m:iexp
j:ivar
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.
' 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.
Store and Recall are for strings what BSave/BLoad or BPut/Bget are for general arrays and memory areas.
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
{Created by Sjouke Hamstra; Last updated: 16/07/2015 by James Gaite}