03 August 2023

File I/O using Get and Put

This time I want to put the focus on the Get and Put commands that were first introduced in GFA-BASIC 32. These are high-performance commands to save and load the contents of a variable in a binary format. The syntax for these commands:

Get #n, [index%], variable
Put #n, [index%], variable

The index% parameter specifies the record number, but it is only required for a Random mode file. A Random mode file needs a Len value when opened, which is the last parameter in the Open command:

Open pathname [For mode] [Access access] [share] [Commit] [Based 0/1] As [#]filenumber [Len=reclength]

When the index% parameter is used, the file pointer is positioned at index% * Len. For all other files (mode is Output, Input, Binary, Update, or Append) the index% parameter should be omitted and:

Get reads from the current file position.
Put writes the variable at the current position.

Depending on the Based setting in the Open command, which is 1 by default, the file position is actually calculated as (index% - Based) * Len. If Len is omitted the record length is set to 1. Consequently, when index% is used with a Len setting of 1, the index% value is multiplied by 1. Therefor, it is important to omit the parameter in non-Random files, so most often these commands are used like this:

Put # 1, , a$
Get # 1, , a$

The binary format
The contents of the variable is not saved as a string representation of the value like Print # and Write # do. The contents of the variable is saved in the same format as it is stored in memory. For instance, a Float variable is stored in 4 bytes and Put only copies those 4 bytes to the opened file. Get reads the 4 bytes and moves them directly into the Float variable. Saving a the floating-point as a 4 byte binary prevents the rounding otherwise necessary for writing a string representation using Write # or Print #. (BTW Print # isn't a suitable file I/O command unless you are saving a string.)

If the variable being written is a numeric Variant, Put writes 2 bytes identifying the variable type of the Variant and then writes the binary representation of the variable. So, a Variant holding a Float is saved in 6 bytes: first 2 bytes identifying the Variant as basSingle (4) and followed by 4 bytes containing the data.
If the Variant holds a string, Put writes 2 bytes to identify the type (basVString) , then 2 bytes specifying the length of the string, followed by the string data.

A variable-length string is saved likewise, only it omits the 2 bytes that specify the datatype. Put writes the length in 2 bytes, directly followed by the string data. The disadvantage is the limitation of 65535 characters, because that's the maximum value that can be stored in 2 bytes (Card).

File mode
Since Put and Get are binary I/O commands you might think they are restricted to Random mode or Binary mode files, but that isn't true. Binary I/O can happen with files opened in all modes, including Output, Input, Update, and Append.These modes do not differ much; opening a file with a certain mode is more of a reminder to the developer than that it defines an I/O operation. It is the file I/O command that defines the input-output format. Print # always writes a string, and Out # always write a binary value, whatever the mode of the opened file. Making a file Random allows Get and Put to read/write to a record directly with out setting the file pointer, otherwise it is the same as Update.

Conclusion
The Get and Put commands are fast binary I/O commands and are an interesting addition to the file I/O commands from previous versions of GFA-BASIC.