Files

From RFO-BASIC! Manual
Manual contents (Statement index)
Language features The basicsArraysData structuresInterrupt routinesUser-defined functions
Interfaces Audio playerBluetoothFilesGraphicsHTMLKeyboardSocketsSQLTelecom
Programming notes Coexisting with Android

RFO-BASIC! programs can use files that are in internal storage or on a removable device such as an SD card. (Android phones often have a microSD card inside. It is an alternative "removable" storage space, though it may be difficult to actually remove it.) With an adapter attached to the USB port, a removable device may also be a thumbdrive. Internal storage and removable devices are both called storage drives. By default, files reside on the base drive. Originally, this is the drive where you installed RFO-BASIC!. You can change the base drive using Menu → Preferences when running RFO-BASIC!.

Files are sequences of bytes. Traditional (8-bit) characters can be stored as bytes. UNICODE characters can be stored as multiple bytes. However, some RFO-BASIC! programs transform these characters.


File systems[edit]

A file system is a set of one or more directories. (Directories are also known as folders.) Each directory is a list of files by name. Within the list there can also be directories, which also have names. Such a directory is a subdirectory of the directory that lists it. Files and directories are known collectively as objects. The name of each object must be unique within its directory.

RFO-BASIC! directories

The base drive (defined at the start of this page) contains a directory named rfo-basic. This base directory exists alongside other directories on the drive such as Pictures and Downloads. The base directory contains several subdirectories:

Directory name Contents RFO-BASIC statements that look there first
rfo-basic/source RFO-BASIC! user-written programs INCLUDE and RUN
rfo-basic/databases SQLite databases The statements in SQL
rfo-basic/data Text and byte files created by RFO-BASIC! programs All other statements
Navigating directories

The / character means that the text to the right of it is an object residing in the directory named to the left of it. For example, in the table above, rfo-basic/source describes an object (it is a directory) named source located inside the directory rfo-basic. (MS-DOS and Windows use \ instead.)

No special navigation is needed to reach a file that is in the usual directory shown in the table above. For example, if there is a text file named a in the usual directory for those files — if it is rfo-basic/data/a — then an RFO-BASIC! program can refer to it simply by name: a. An SQLite database in the usual place, rfo-basic/databases/Employees, could be specified as simply Employees.

You may create subdirectories using MKDIR. For example, within the data directory, you might create a subdirectory exclusively for the text files of a single RFO-BASIC! program, such as at rfo-basic/data/MyApp. If this subdirectory contained a text file named Preferences, you would gain access to it as MyApp/Preferences. If you created another directory for that program's databases, it would be at rfo-basic/databases/MyApp. Then the program could create databases in that subdirectory, such as MyApp/Employees.

You may create subdirectories inside a subdirectory; for example, a single program might have several files with different types of information, and use a separate subdirectory for files of each type. Each use of / travels from a directory to a subdirectory it lists.

An exception is that ../ navigates to the outer directory. For example, a text file statement in an RFO-BASIC! program could specify ../database/x to go out of the data directory (back to the rfo-basic directory) and into the database directory, if a text file named x were stored there for some reason.

File protection

The RFO-BASIC! program always has permission to gain access to the three directories listed in the table above, as well as the rfo-basic base directory. It can use MKDIR to create directories in addition to the three built-in directories, such as ../avatars. It can step outside rfo-basic to navigate to a sibling directory, such as ../../Downloads or ../../Pictures.

However, the program might not have permission to step further out than the user directory containing rfo-basic. It might not have permission to use an absolute pathname, such as the pathname that FILE.ROOT returns, even if the absolute pathname specified a directory to which the program had permission.

The program might not have permission to gain access to a removable device, such as a microSD card. The program can test whether it has permission; see the note at TEXT.OPEN.

Scoped storage

Emerging versions of Android introduce the concept of Scoped Storage, in which a program specifies a file not by using a pathname to a directory but according to the type of information or relation to the program. The concept that this article relies on is called Legacy Storage.

  • Android 10 will incorporate Scoped Storage but will make it a program option. However, a flag must be set in the Manifest in the .APK file to exercise this option. hBasic v1.92 indeed specifies Legacy Storage. There would have to be an RFO-BASIC! version after v1.92 to support programs that select Scoped Storage.
  • Android 11 removes the option of Legacy Storage. RFO-BASIC!'s file statements will need redesign, and some operations, such as exchanging information between RFO-BASIC! programs and programs created by other apps, will become difficult or impossible.

General rules[edit]

Alphabetic case[edit]

Objects (files and directories) have names, which the program must specify to open the file in order to gain access to its contents.

  • Files stored on SD cards and thumbdrives use the Microsoft FAT file system, in which a capital letter matches the corresponding lowercase letter. A file named PEOPLE can be used by specifying People or people or other combinations of uppercase and lowercase. But when a file is created, its name is stored in the directory with capital or lowercase letters as the program specifies.
  • Files stored in the phone's internal storage use the Linux file system, in which the case of letters is important. Each of the variations given above would refer to a different file.

Programmers need to pay attention to this because it can lead to unusual errors. For example, if an RFO-BASIC! program capitalized all filenames, in order to be tolerant of different styles of typing, then a file with lowercase in its name, which was accessible on a removable drive, would suddenly become inaccessible if moved to the phone's internal storage. To prevent errors, RFO-BASIC! programs should always assume a filename could contain lowercase letters, and should match case exactly in file paths and names.

Text files and byte files[edit]

RFO-BASIC! programs can open files as lines of text, using the statements in the section Text files, or as sequences of 8-bit bytes, using the separate statements in the section Byte files.

Text files and byte files each have a file position, the location in the file where the next read or write will occur. The initial position depends on the form of the TEXT.OPEN or BYTE.OPEN statement, and on whether the file exists.

End-of-file[edit]

End-of-file means that the file position is at the end of the entire contents of the file. When writing or appending to a file, the position is always at end-of-file. When reading a file, end-of-file means there is nothing more to read. Typically, the program has a loop that reads units of the file, and reaching end-of-file breaks out of the loop.

When RFO-BASIC! reads information from a file, it does not check whether doing so reached end-of-file. If you know where end-of-file is, such computation would be unnecessary. Instead, end-of-file is declared only if the program continues to read and fails. The TEXT.EOF and BYTE.EOF statements test this condition.

Managing directories[edit]

FILE.MKDIR[edit]

Create a subdirectory

Synopsis
FILE.MKDIR <path_sexp>
Description

The FILE.MKDIR statement (you can simply write MKDIR) creates a directory. The <path_sexp> argument specifies the new directory. If <path_sexp> does not contain /, then it is simply the name of the new directory, which will be a subdirectory of rfo-basic/data. If <path_sexp> does contain /, then everything after the last / is the name of the new directory and everything before is the path of the directory in which to create it.

Examples
MKDIR "Homes"

This creates a directory Homes inside the assumed directory rfo-basic/data.

MKDIR "../libraries"

This path goes up to the rfo-basic directory and creates a subdirectory of it, named libraries, alongside its existing subdirectories, data, databases, and source.

FILE.DELETE[edit]

Remove an object (file or subdirectory) from a directory

Synopsis
FILE.DELETE <result_lvar>, <path_sexp>
Description

The FILE.DELETE statement deletes a file or a directory specified by <path_sexp>. If <path_sexp> does not contain /, then it is simply the name of the object to be deleted from rfo-basic/data. If <path_sexp> does contain /, then everything after the last / is the name of the object to delete and everything before is the directory in which to look for it.

The statement sets <result_lvar> to a nonzero value if the specified object existed and was deleted. It sets <result_lvar> to zero if the object did not exist.

Example
FILE.DELETE success, "MyTempFile"

FILE.RENAME[edit]

Rename an object

Synopsis
FILE.RENAME <oldpath_sexp>, <newpath_sexp>
Description

The FILE.RENAME statement (you can simply write RENAME) looks for an object whose name is the string at <oldpath_sexp> and renames it to <newpath_sexp>. If the object was a directory, it remains a directory; if it was a file, it remains a file.

The parameters can use the / character to specify not only the name of the object but its location. If a parameter omits /, it is assumed to be in the rfo-basic/data directory.

If the specified or implied directory in <oldpath_sexp> is not the same as that in <newpath_sexp>, then the object is moved from the first directory to the second directory.

It is an error if an object specified by <oldpath_sexp> does not exist in the specified or implied directory. The program can query the directory, using the statements described in the next section, to ensure that this error will not occur. If an object already exists with the desired new name, it is replaced.

Examples
RENAME "MyTempFile", "Preferences"

This changes the name of a file in the rfo-basic/data directory.

RENAME "../../Bluetooth/newdata" "1"

Assuming a file named newdata has been imported from another phone using Bluetooth, this statement moves it from the Bluetooth directory alongside the rfo-basic directory into rfo-basic/data and names it 1.

Querying directories[edit]

The statements in this section retrieve information about the contents of a directory. In all cases, the pathname starts in the directory rfo-basic/data. To query the RFO-BASIC! directory for databases, the program could use a string such as "../databases/Employees".

FILE.EXISTS[edit]

Determine if a specified object exists

Synopsis
FILE.EXISTS <result_lvar>, <path_sexp>
Description

The FILE.EXISTS statement checks to see if an object exists with the name, and in the directory, specified by <path_sexp>. It sets <result_lvar> to a nonzero value if the object exists, and to zero if it does not.

Example
FILE.EXISTS success, Filename$
IF success THEN FILE.DELETE Filename$

A program should check for the existence of a file before using FILE.DELETE to delete it, to avoid the possibility of a run-time error.

FILE.TYPE[edit]

Determine the existence and type of an object

Synopsis
FILE.TYPE <type_svar>, <path_sexp>
Description

The FILE.TYPE statement checks to see if an object exists with the name, and in the directory, specified by <path_sexp>, as FILE.EXISTS does. However, FILE.TYPE returns, as a one-character string, an indicator describing the type of the object.

Indicator Meaning
d (Directory) The object is a directory
f (File) The object is a file
o (Object) The object is a special object
x There is no such object
Example

The following example is another way to pre-test before trying to delete a file. This code also ensures that the program does not delete an entire directory.

FILE.TYPE ind$, Filename$
IF ind$="f" THEN FILE.DELETE Filename$

FILE.SIZE[edit]

Obtain the size of a file

Synopsis
FILE.SIZE <size_nvar>, <path_sexp>
Description

The FILE.SIZE statement determines the size, in bytes, of the file specified by <path_sexp>, returning the result in <size_nvar>. It is an error if such a file does not exist.

Example
FILE.TYPE ind$, Filename$
IF ind$="f" THEN FILE.SIZE Result, Filename$ ELSE Result = -1

This example ensures that Filename$ specifies an existing file (not a directory). If so, it places in Result the length of the file. Files may have a length of 0. If there is no such file, it sets Result to -1 to indicate this fact.

FILE.DIR[edit]

Obtain a directory listing in an array

Synopsis
FILE.DIR <path_sexp>, Array$[]{, <dirmark_sexp>}
Description

The FILE.DIR statement (you can simply write DIR) creates a listing of the specified directory and places it in the specified string array. If the array exists, it is overwritten; otherwise, it is created. The result is a one-dimensional array in which each element is the name of one object in the directory. The descriptions are sorted so that directories are grouped before files; within each group, entries are sorted alphabetically. The <dirmark_sexp> parameter is a string appended to an entry if the object is a directory. If <dirmark_sexp> is omitted, FILE.DIR appends (d). The program can specify "" as <dirmark_sexp> to avoid any mark for directories.

Example
DIR "../databases", dbnames$[], "/"
ARRAY.LENGTH nObjects, dbnames$
FOR i=1 TO nObjects
 PRINT dbnames$[i]   % Or take some other desired action on each object
NEXT i

This example produces, in dbnames$[], a list of the contents of RFO-BASIC!'s database directory. If it contains subdirectories, they are listed first, each ending with /. The example then uses ARRAY.LENGTH to obtain the number of entries, which may be zero.

FILE.ROOT[edit]

Get the absolute pathname of the RFO-BASIC! data directory

Synopsis
FILE.ROOT <path_svar>
Description

The RFO-BASIC! directories are never in the phone's root directory, but instead are several directories removed from the root. The FILE.ROOT statement obtains an absolute pathname of the base directory. (Absolute pathnames start with /, denoting the phone's root directory.

RFO-BASIC! programs never use absolute pathnames; they use pathnames relative to the RFO-BASIC! base directory. However, a program might use FILE.ROOT to generate an absolute pathname of its files to pass to some other program.

Example
FILE.ROOT S$

An inexpensive Samsung phone set S$ to /storage/emulated/0/rfo-basic/data. This is because, when RFO-BASIC! is installed in the phone's internal storage, the phone places it in "emulated drive 0." This result will be different on different devices, and different still if the base directory is on a removable device.

Text files[edit]

Text files are files that contain lines of text. The text file statements of RFO-BASIC! read or write a single line at a time. A line ends with the character CR (carriage return) or LF (line feed) or both.

The characters in a line can be single-byte characters, such as USASCII codes, or can be UNICODE characters.

Every open file has a position within it where the next text will be read or written. See the table at TEXT.OPEN.

Buffering

As you write text to a file, the text goes into an intermediate buffer of 8192 characters. It is only when this buffer becomes full that it is written to the permanent file. If your program ends without closing the file using TEXT.CLOSE, then text in the intermediate buffer might be discarded and might not appear in the file the next time you open it.

TEXT.OPEN[edit]

Open a text file

Synopsis
TEXT.OPEN {R|W|A}, <handle_nvar>, <path_sexp>
Description

The TEXT.OPEN statement opens a text file. The program must open a file before it can deal with the file's contents or write new contents to it. The program specifies this file using the <handle_nvar> that TEXT.OPEN returns. The <path_sexp> parameter gives the name of the file. It is assumed to reside in the data directory. See the start of this article for specifying some other directory, and for the rules on capitalization.

One of three letters must follow TEXT.OPEN. This is typed literally and is not a string expression. This is the mode in which to open the file:

Mode Effect if file exists Effect if file doesn't exist
R (Read) Reads from the start of the file The file cannot be read. The statement sets the handle to -1. Details about the error may be available using GETERROR$().
W (Write) Destroys any existing file by the specified name, creates a new (empty) file, and prepares to write to the start of the file Creates a new (empty) file and prepares to write to the start of it
A (Append) Prepares to write new text at the end of the file Creates a new (empty) file and prepares to write to the start of it
Testing access to a device

A TEXT.OPEN W statement could set <handle_nvar> to -1 instead of to a file handle. This would indicate that the program does not have permission to write to the device (for example, the device might be a microSD card). There is no way for an RFO-BASIC! program to override lack of permission, nor to modify the user's list of permissions. (It might also mean that a microSD card is totally full.)

A TEXT.OPEN R statement also sets <handle_nvar> to -1 if the program lacks access permission, but coding TEXT.OPEN W and testing <handle_nvar> is a less ambiguous test for this condition.

Example

See the examples below.

TEXT.CLOSE[edit]

Close a text file

Synopsis
TEXT.CLOSE <handle_nexp>
Description

The TEXT.CLOSE statement closes the file with the specified handle that was opened by TEXT.OPEN.

Example
TEXT.OPEN R, hdl, "MyFile"
IF hdl = -1 THEN PRINT "The file doesn't exist!" : END
TEXT.CLOSE hdl

This example simply opens and closes a specified file. It is good for nothing except to see if the file exists.

TEXT.READLN[edit]

Read a line of text from a file

Synopsis
TEXT.READLN <handle_nexp>,{<line_svar>}...
Description

The TEXT.READLN statement reads one or more lines from the file with the specified handle. That file must have been successfully opened by TEXT.OPEN. As many lines are read as the program provides string variables. After reading, the file is positioned beyond the last line that was read.

If the file's position is at end-of-file, then the string "EOF" is returned and the position remains at end-of-file. (See also the TEXT.EOF statement.) The file is not automatically closed; the program should use TEXT.CLOSE.

Example

This example reads each line of the specified file and prints it on the Console. The example is refined later in this article.

TEXT.OPEN R, hdl, "MyFile"  % Also test hdl > -1, as done above
DO
 TEXT.READ hdl, str
 PRINT str                  % Or do whatever you want with the line
UNTIL str = "EOF"           % EOF has been PRINTed to the Console too
TEXT.CLOSE hdl

TEXT.EOF[edit]

See if a text file's position is at the end

Synopsis
TEXT.EOF <handle_nexp>, <atend_lvar>
Description

The TEXT.EOF statement returns in <atend_lvar> the value 0 if the file with the specified handle is not at end-of-file, and a nonzero value if it is at end-of-file.

Using TEXT.EOF is vital to distinguish between TEXT.READ returning "EOF" to indicate end-of-file, and returning "EOF" because it actually read a line of text that was "EOF".

When reading a file, the position is not end-of-file until the program has read all the text from the file and then attemped one more read (which returns "EOF"). When writing or appending, the file's initial position is end-of-file.

Example

This example refines the previous example so that an "EOF" that simply denotes end-of-file is not PRINTed.

TEXT.OPEN R, hdl, "MyFile"  % Also test hdl > -1, as done above
DO
 TEXT.READ hdl, str
 IF str="EOF" THEN
  TEXT.EOF hdl, IsEnd
  IF IsEnd <> 0 THEN D_U.BREAK   % Exit the loop
 ENDIF
 PRINT str                  % Or do whatever you want with the line
UNTIL 0
TEXT.CLOSE hdl

TEXT.WRITELN[edit]

Write a line to a text file

Synopsis
TEXT.WRITELN <handle_nexp>{, <parameters>}

The TEXT.WRITELN statement is like the PRINT statement but writes information to the file with the specified handle, rather than to the Console.

The parameters can be numeric or string expressions (including string literals). For numeric expressions, TEXT.WRITELN prints their string representation. The program can use functions such as USING$() to get precise control over the representation of numbers.

The parameters are separated by comma or semicolon. If a parameter is followed by a comma, the string is assembled on the output line, followed by blanks. If it is followed by semicolon, the string is assembled on the output line without separation from the next string.

If the final parameter is not followed by a comma or semicolon, the line is complete and is written to the file. If the final parameter is followed by a comma or semicolon, the statement ends without actually writing the output line. Subsequent TEXT.WRITELN statements can add text to the output line. A TEXT.WRITELN statement with nothing following the <handle_nexp> parameter simply ends the output line, writing the line to the text file; if previous TEXT.WRITELN statements did not provide anything to write, this statement writes an empty line.

Random access to a text file[edit]

If TEXT.OPEN opens a file in mode R (Read), RFO-BASIC! lets your program move the position of a text file to a specified line number. The first line number is 1. As the program reads lines from a text file, RFO-BASIC! builds a table indicating the start of each line in the file. If you try to jump to a line number beyond the last one you have read or written, RFO-BASIC! reads through the file, further building the table. The ability to use line numbers lets you read lines from a text file in an order other than sequential order.

If TEXT.OPEN opens a file in mode A (Append), line 1 refers to the first new line you write. It is not possible to move the position to one of the preexisting lines preceding the appended text. However, after appending the desired text, you could close the file and reopen it in mode R to read its lines in arbitrary order.

RFO-BASIC! does not let programs write lines in other than sequential order, as it is problematic what happens when a line is replaced by a line of a different length.

Use of marks and limits affects the ability to gain random access to a text file.

TEXT.POSITION.GET[edit]

Get a text file's current line position

Synopsis
TEXT.POSITION.GET <handle_nexp>, <position_nvar>
Description

The TEXT.POSITION.GET statement returns, in <position_nvar>, the line position of the text file whose handle is <handle_nexp>.

TEXT.POSITION.SET[edit]

Change a text file's current line position

Synopsis
TEXT.POSITION.SET <handle_nexp>, <position_nexp>
Description

The TEXT.POSITION.SET statement moves the position of a text file, whose handle is <handle_nexp>, to the line number given in <position_nexp>. If the specified line number is past the end of the file, the file is moved to end-of-file, and using TEXT.POSITION.GET will obtain the number of lines in the file, plus 1.

Reading an entire file[edit]

Two statements place the entire contents of a file in a string variable.

Synopses
GRABURL <text_svar>, <path_sexp>{, <timeout_nexp>}
GRABFILE <text_svar>, <path_sexp>{, <unicode_lexp>}
Description

The GRABURL statement obtains an entire file across the Internet. It can also obtain an entire local file. When grabbing a local file, the specified time-out interval is meaningless.

The GRABFILE statement also obtains a local file. Its third parameter is a logical variable. If <unicode_lexp> is 0, then GRABFILE sets each character of <text_svar> to one character from the file. This is appropriate if the file contains 8-bit bytes (binary data) or exclusively 8-bit characters, such as ASCII. If <unicode_lexp> is TRUE (any nonzero value), then GRABFILE reads 16-bit UNICODE characters and sets each character of <text_svar> to one UNICODE character from the file. This makes the characters easier to process.

Both statements may set <text_svar> to the empty string. This may mean the file does not exist, or that there was an error reading it. The program can call GETERROR$() to get details on the error. It may also mean that the file exists and was readable but contained no information. In this case, GETERROR$() returns "No error".

Examples

To edit a text file, the following two statements are often used together:

GRABFILE text$, "MyJournal.txt"
TEXT.INPUT EditedText$, text$, "Type your changes below"

The string returned by GRABURL or GRABFILE may be supplied to the SPLIT statement to produce a string array where each element is a single line from the file.

Byte files[edit]

Byte files can be any file, including

  • Files that contain lines of text, which could also be opened using the RFO-BASIC! statements for text files (.txt).
  • Music (.mp3 or .wav).
  • Pictures (.jpg or .png or .gif)
  • Formatted documents (.rtf or .pdf).

When an RFO-BASIC! program opens a file as a byte file, it prepares to read or write individual 8-bit bytes, codes from 0 through 255. If a text file contains multibyte characters, such as in UNICODE, they will appear as a sequence of bytes; a single byte read or written will not be an entire character.

BYTE.OPEN[edit]

Open a byte file

Synopsis
BYTE.OPEN {R|W|A}, <handle_nvar>, <path_sexp>
Description

The BYTE.OPEN statement opens a byte file. The program must open a file before it can deal with the file's contents or write new contents to it. The program specifies this file using the <handle_nvar> that BYTE.OPEN returns. The <path_sexp> parameter gives the name of the file. It is assumed to reside in the data directory. See the start of this article for specifying some other directory, and for the rules on capitalization.

One of three letters must follow BYTE.OPEN. This is typed literally and is not a string expression. This is the mode in which to open the file:

Mode Effect if file exists Effect if file doesn't exist
R (Read) Reads from the start of the file The file cannot be read. The statement sets the handle to -1. Details about the error may be available using GETERROR$().
W (Write) Destroys any existing file by the specified name, creates a new (empty) file, and prepares to write to the start of the file Creates a new (empty) file and prepares to write to the start of it
A (Append) Prepares to write new text at the end of the file Creates a new (empty) file and prepares to write to the start of it
Example

See the examples below.

BYTE.CLOSE[edit]

Close a byte file

Synopsis
BYTE.CLOSE <handle_nexp>
Description

The BYTE.CLOSE statement closes the file with the specified handle that was opened by BYTE.OPEN.

Example
BYTE.OPEN R, hdl, "MyFile"
IF hdl = -1 THEN PRINT "The file doesn't exist!" : END
BYTE.CLOSE hdl

This example simply opens and closes a specified file. It is good for nothing except to see if the file exists.

BYTE.READ.BYTE[edit]

Read one or more bytes from a byte file

Synopsis
BYTE.READ.BYTE <handle_nexp>{, <byte_nvar>}...
Description

The BYTE.READ.BYTE statement reads one or more bytes from the file with the specified handle. That file must have been successfully opened by BYTE.OPEN. As many bytes are read as the program provides numeric variables. Each variable is set to a value from 0 through 255. After reading, the file is positioned beyond the last byte that was read.

If the file's position was at end-of-file, then the position remains at end-of-file. (See also the BYTE.EOF statement.) The file is not automatically closed; the program should use BYTE.CLOSE.

Unlike TEXT.READLN, BYTE.READ.BYTE does not return values that indicate that it reached end-of-file. Unless you know the exact length of the byte file, the program should read one byte at a time and use BYTE.EOF to test for end-of-file.

Example

The following example reads an entire file and prints it on the Console:

BYTE.OPEN R, handle, "waypoints"
IF handle = -1 THEN
!  Report that the file could not be opened and do not continue
ENDIF
DO
 BYTE.READ.BYTE handle, i
 BYTE.EOF handle, WasEOF
 IF WasEOF THEN D_U.BREAK  % Exit loop on EOF; "i" is not file data
 PRINT CHR$(i)
UNTIL 0
BYTE.CLOSE handle

This example does not faithfully print multi-byte (UNICODE) characters, but tries to print separately each byte of the character. See the discussion of GRABFILE.

Instead of PRINTing each character, you could use BYTE.WRITE.BYTE to write it to a different file. However, to make an exact copy of a byte file, BYTE.COPY is thousands of times faster.

BYTE.READ.BUFFER[edit]

Read bytes from a byte file into a string variable

Synopsis
BYTE.READ.BUFFER <handle_nexp>, <count_nexp>, <buffer_svar>
Description

The BYTE.READ.BUFFER statement reads bytes from the file with the specified handle. The <count_nexp> parameter specifies how many bytes to read. If <count_nexp> is 0, nothing happens.

RFO-BASIC! strings contain 16-bit characters. But BYTE.READ.BUFFER loads into each character of <buffer_svar> one 8-bit character from the byte file. (The high byte of the character read into <buffer_svar> is 0.) If you are using BYTE.READ.BUFFER to read multibyte characters, such as UNICODE characters, a single UNICODE character will occupy multiple characters of <buffer_svar>. The ASCII() or UCODE() functions can be used to create a string that properly renders the UNICODE text.

The <buffer_svar> variable (or a substring of it, extracted using MID$()) can also be passed to DECODE$() to convert it to an RFO-BASIC! string.

BYTE.WRITE.BYTE[edit]

Write to a byte file

Synopsis
BYTE.WRITE.BYTE <handle_nexp>,{ <byte_nexp>}...{, <string_sexp>}
Description

The BYTE.WRITE.BYTE statement writes various things to the file with the specified handle. The handle may be followed by one or more numeric expressions (in which case, each expression is evaluated and causes a single byte to be written to the file), which may be followed by a string expression (in which case, each character of the string is written to the file as one byte). To simply write the characters of a string to a byte file, it is better to use BYTE.WRITE.BUFFER.

BYTE.WRITE.BUFFER[edit]

Write a string to a byte file

Synopsis
BYTE.WRITE.BUFFER <handle_nexp>, <buffer_sexp>
Description

The BYTE.WRITE.BUFFER statement writes an RFO-BASIC! string to a byte file with the specified handle. The number of bytes written is the length of the string expression <buffer_sexp>.

RFO-BASIC! strings contain 16-bit characters. But BYTE.WRITE.BUFFER writes only the low 8 bits of each character as one byte in the byte file. The high 8 bits have no effect.

The program can build the buffer in several ways:

  • The ENCODE$() function converts a string of UNICODE characters to 8-bit characters so that BYTE.WRITE.BUFFER will write correct UNICODE characters to the byte file.
  • The RFO-BASIC! program can create binary data in a string variable by using the CHR$() function to create 8-bit characters representing any binary value from 0 through 255.
  • If <buffer_sexp> is certain to contain only 8-bit characters (such as ASCII characters), then it can be output directly using BYTE.WRITE.BUFFER. The effect is the same as with TEXT.WRITELN except that BYTE.WRITE.BUFFER does not write a line-termination character unless the program appends one to the end of <buffer_sexp>.

BYTE.COPY[edit]

Copy an entire byte file

Synopsis
BYTE.COPY <handle_nexp>, <pathname_sexp>
Description

The BYTE.COPY statement reads from a file that the RFO-BASIC! program has already successfully opened (that is, <handle_nexp> must be a valid byte file handle, and not -1). Beginning at this file's current position, it creates a new file with the given pathname and copies the remainder of the input file to this new file. BYTE.COPY then performs BYTE.CLOSE <handle_nexp> (as well as closing the new file).

The position of <handle_nexp> is at the start of the file when you use BYTE_OPEN R. However, if you have read from the file, then BYTE.COPY only copies the subsequent bytes from the file. If you want to make a complete copy of the file, set the file position back to the start of the file with a sequence such as:

BYTE.POSITION.SET InputHandle,0
BYTE.COPY InputHandle, "Newfile"

Do not code a loop with BYTE.READ.BYTE and BYTE.WRITE.BYTE, unless your program sometimes makes conversions on the file's contents instead of copying them literally. To make an exact copy, BYTE.COPY is thousands of times faster.

BYTE.TRUNCATE[edit]

Truncate a byte file at the current position

Synopsis
BYTE.TRUNCATE <handle_nexp>, <length_nexp>

The BYTE.TRUNCATE statement operates on files successfully opened in mode W (Write) or A (Append). That is, <handle_nexp> must not be -1. The statement indicates the desired total size of the file in bytes. This must result in a shortening of the file; it is meaningless to specify a larger size than the number of bytes you wrote to the file, so <length_nexp> must be lower than the current size of the file (the current file position minus 1).

BYTE.TRUNCATE also closes the file.

The effect of BYTE.TRUNCATE is to declare that some of the bytes most recently written to the file should not be saved. Truncating a file to 0 bytes means the file will exist but will contain no bytes.

Reading and writing numeric data[edit]

Two statements read and write numbers to a byte file. In the file, each number takes up 8 bytes. In this way, an RFO-BASIC! program can store numeric values and read them back later, without potential problems, such as round-off error, that might occur when using a text file to store their text representation. This is the only recommended use for these statements; the numbers are written in their internal RFO-BASIC! format, which is not guaranteed to be the same as the format that any other program uses.

Synopses
BYTE.READ.NUMBER <handle_nexp>{, <number_nvar>}...
BYTE.WRITE.NUMBER <handle_nexp>{, <number_nexp>}...
Description

The BYTE.READ.NUMBER statement reads one or more numbers from the byte file with the specified handle, and places them in the numeric variables in the order in which it reads them. When reading numbers, the byte file position should be the exact position where BYTE.WRITE.NUMBER wrote them. If BYTE.READ.NUMBER reaches end-of-file, it sets the variable being read, and all subsequent variables in the parameter list, to -1. The program should not use -1 as an end-of-file indicator, because the number in the byte file might actually be -1. Instead, the program should know how many numbers the byte file contains, or should read one at a time and use BYTE.EOF to see if end-of-file was reached after each.

The BYTE.WRITE.NUMBER statement writes one or more numbers to the byte file with the specified handle. The numbers to be written are given by the numeric expressions in the parameter list, and BYTE.WRITE.NUMBER writes them in the sequence in which they are listed.

Marks and limits[edit]

When TEXT.OPEN or BYTE.OPEN opens a file, the following things happen:

  • The mark is set to the start of the file.
  • The limit is set to the entire size of the file.
  • The mark and limit together constitute a window into the file.
  • The window is read into RFO-BASIC!'s internal memory.

The programmer need not bother with these concepts except when opening a file that is larger than RFO-BASIC!'s entire internal memory. An attempt to read or reposition the file would fail, and GETERROR$() would obtain the string "Out of memory". In this case, the programmer must specify a limit that fits in internal memory. The programmer may also specify a mark so that the portion of interest of the file is not the start of the file.

  • You can define a window in a text file with TEXT.POSITION.MARK
  • You can define a window in a byte file with BYTE.POSITION.MARK
Synopses
BYTE.POSITION.MARK {{<handle_nexp>} {, <limit_nexp>}}
TEXT.POSITION.MARK {{<handle_nexp>} {, <limit_nexp>}}
Description

This statement sets a window into the file whose handle is <handle_nexp>. It declares that the current position in the file is the mark, and the number of bytes in <limit_nexp> is the limit.

Both parameters are optional. If <handle_nexp> is omitted, the default file is the file that was opened most recently. The specified or implied file must be a file opened in mode R (Read). TEXT.POSITION.MARK can only be used on text files, and BYTE.POSITION.MARK can only be used on byte files.

If you omit <limit_nexp>, then the statement does not change the limit.

The TEXT.POSITION.SET and BYTE.POSITION.SET statements, which enable random access to a file, cannot be used to jump to a position before the mark.

In addition, you cannot read nor jump past the limit. A statement that attempts this fails, and GETERROR$() returns "Invalid mark".

Sources[edit]

  • De Re BASIC! (v1.91), pages 104-117.