Chapter 8
Disk Files
When you tell OS-9 or BASIC09 to store (save) data on a disk, it
stores the data in a logical block called a
file.
The term logical
means that, although the system might store portions of a file's
data in several different disk locations, it keeps track of every
location and treats the scattered data as though it occupied a
single block. It does this automatically and you never need to
worry about how the data is stored. File data can be binary
data, textual data (ASCII characters), or any other useful
information.
Because OS-9 handles all hardware input/output devices (disk
drives, printers, terminals, and so on) in the same manner, you
can send data to any of these devices in the same way. This
means you can send the same information to several devices by
changing the path the data follows. Fbr example, you can test a
procedure that communicates with a terminal by transferring
data to and from a disk drive.
BASIC09 normally works with two types of files-sequential
files and random access files. The following chart shows fileaccess options, their purposes, and the keywords with which to
use them:
Types of Access for Files
Access Function Use with
Type
DIR Opens a directory file for reading. OPEN
Use only with READ.
EXEC
Specifies that the file to open or OPEN
create is in the execution directory, CREATE
rather than the data directory.
READ
Lets you read data from the OPEN
specified file or device. CREATE
WRITE
Lets you write data to the specified OPEN
file or device. CREATE
UPDATE
Lets you read data from and write
data to the specified file or device.
OPEN
CREATE
BASIC09 Reference
Sequential Files
Sequential files send or receive (WRITE or READ) textual data
in order, the second item following the first, and so on. You can
access sequential data only in the same order as you originally
stored it. To read from or write to a particular section of a file,
you must first read through all the preceding data in the file,
starting from the beginning.
BASIC09 stores sequential file data as ASCII characters. Each
block of data is separated by a
delimiter
consisting of a carriagereturn character (ASCII Character 13). Because BASIC09 uses
this delimiter to determine the end of a
record,
sequential files
can contain records of varying length.
Use the WRITE and READ commands to store and retrieve data
in sequential files. A WRITE command causes BASIC09 to
transfer specified data to a specified file, ending the data with a
carriage return. A READ command causes BASIC09 to load
from the specified file the next block of data, stopping when it
reaches a carriage return.
Sequential File Creation, Storage, and Retrieval
BASIC09 uses the CREATE command to establish both sequential and random access files. A CREATE statement contains:
The keyword CREATE.
· A path number variable in which BASIC09 stores the
number of the path it opens to the new file.
· A comma, followed by the name of the file to create.
An optional colon, followed by the access mode. If you do
not specify an access mode, BASIC09 automatically
opens the created file in the UPDATE mode.
8-2
Disk Files / 8
The following procedure shows how to create a file and write
data into it:
PROCEDURE makefile
ODIM PATH;BYTE 0 establishes a variable
OREM for the path number to the file
OCREATE #PATH,"test";WRITE 0 creates the file TEST
OWRITE #PATH,"This
i5
a test" 0 writes data to the file
pWRITE #PATH,"of sequential files."(* writes another line of data
pCLOSE #PATH 0 closes the path to the file
OSHELL "LIST TEST" (* displays the file contents
pEND
The first line of the procedure dimensions a variable (Path) to
hold the number of the path that CREATE opens. This variable
should be of byte or integer type.
When you establish a new file with CREATE, you automatically
open a path to the file. You do not need to use the OPEN
command.
The preceding procedure writes two lines into a file named Test.
It then closes the path and uses the OS-9 LIST command to display the contents of the newly created file. You see that the data
is successfully stored on disk.
The next procedure shows how to reopen an existing file for
sequential access, read the contents of the file, and append data
to the end of the file.
The only way to move the
file
pointer to the end of a sequential
file is to read all the data already in the file. Once the pointer is
at the end of the file, you can add data.
PROCEDURE append
ODIM PATH;BYTE 0 dimension variable to hold the number of the
OREM path to the opened file.
DOPEN #PATH,"test";UPDATE (* open file for reading and writing,
DREAD #PATH,line$ 0 read the first element of the file.
DREAD #PATH,line$ 0 read the next (the last) element.
OWRITE #PATH,"This is a test" 0 write one new line to the file.
OWRITE #PATH,"of appending to a sequential file." 0 write another.
OCLOSE #PATH 0 close the path
OSHELL "LIST TEST" 0 display the file with the new lines.
REND
8-3
BASIC09 Reference
Because the Test file already exists, this procedure uses OPEN
to establish a path to the file. It uses the UPDATE mode of file
access because it needs to both read from and write to the file.
The two READ statements read the file's contents and, as a
result, move the file pointer to the end of the file. The WRITE
statements then append two new lines. After closing the path,
the procedure calls on the OS-9 LIST command to display the
contents of the file, with its appended lines.
Changing Data in a Sequential File
You can also change data anywhere in a sequential file. However, if your changes are longer than the original data, the operation destroys part of the file. To change data in a sequential
file, read the data preceding what you want to change, and write
the new data to the file in this manner:
PROCEDURE replace
ODIM PATH:BYTE
OOPEN #PATH,"test";UPDATE
DREAD #PATH,line=
DREAD #PATH,lines
OWRITE #PATH,"Let's put new" (* write over existing 3rd and
OWRITE #PATH,"words into the old sequential file." (* 4th lines.
OCLOSE #PATH
OSHELL "LIST TEST"
REND
Notice that the total amount of data in the two new lines is
exactly the same as in the two old lines. You can replace an
existing line with fewer characters by padding the new data
with spaces. However, if you try to replace existing lines with
longer lines, the new lines write over and destroy other data in
the file.
8-4
Disk Files / 8
INPUT and Sequential Files
Although you can also use the INPUT command with sequential
`' files, doing so might put unwanted data into them. When a pro
- cedure encounters INPUT, it suspends execution and sends a
question mark (?) to the screen. This feature makes INPUT both
an input and output statement. Therefore, if you open a file
using the UPDATE mode, INPUT writes its prompts to the file,
destroying data. If you specify text to be displayed with the
INPUT command, INPUT writes this text to the file also.
Random Access Files
Random access files store data in fixed- or equal-length blocks.
Because each record in a specific file is the same size, you can
easily calculate the position of a record.
For instance, suppose you have a file with a record length of 50bytes (or characters). To access Record 10, multiply the record
number (10) by the record length (50) and move the file pointer
to the calculated position (500).
A random access file sends and receives data (using PUT and
GET) in a binary form, exactly as BASIC09 stores it internally.
This feature minimizes the time involved in converting the data
to and from ASCII representation, as well as reducing the file
space required to store numeric data. You position the random
access file pointer using SEEK. Compared to sequential file
access, random file access using GET and PUT is very fast.
Using random access commands, you can store and retrieve individual bytes, strings of bytes, individual elements of arrays or
total arrays with one PUT or GET command. When you GET a
structure, you recover the number of bytes associated with that
type of structure.
This means when you GET one element of byte type data, you
read one byte. When you GET one element of real type data, you
read five bytes. If you GET an array, you read all the elements of
the array. This potential for reading entire arrays at once can
greatly speed disk access.
As well as moving the file pointer to the beginning of individual
records, you can also move it to any position within a record and
begin reading or writing one or more bytes from that point.
8-5
BASIC09 Reference
Creating Random Access Files
You create and open random access files in the same way you
create and open sequential files. The only differences are in the
commands you use to store and retrieve the data and in the
manner you keep track of where elements, or records, of a file
begin and end.
Before you can write data to a random access file, you must
either CREATE it or open it in the WRITE or UPDATE mode.
Once you have a path open to an existing file, use PUT to write
data into the file. If you open the file in the READ or UPDATE
mode, you can then use the GET command to retrieve data from
the file.
The PUT command can use only one parameter, the name of the
data element to store. The parameter can be a string, a variable,
an array, or a complex data structure.
Before storing data, you must devise a method to store it in
blocks of equal size. Knowing the unit size lets you later retrieve
the data in its original form. The following procedure shows one
way to do this:
w
PROCEDURE putget
OREM This procedure creates a file named Test1, reads 10 data lines,
OREM PUTS them into the file, then closes the file. Next it
OREM opens the file in the READ mode, GETS stored lines and lists
OREM them on the display screen.
ODIM LENGTH:BYTE
ODIM NULL:STRINGf253
ODIM LINE:STRINGI25l
ODIM PATH:BYTE
OLENGTH=25
ONULL-""
OBASE 0
DON ERROR GOTO 10
ODELETE "test1" ( if the file exists, delete it.
1000N ERROR
OCREATE #PATH,"te5t1":WRITE ( create a file named test1.
OFOR T=0 TO 9
OSEEK #PATH,LENGTH*T ( find beginning of each file.
DREAD LINE: (* read a line of data.
OPUT #PATH,LINES ( store the line in the file.
ONEXT T
s-s
Disk Files / 8
OCLOSE #PATH ( close the file.
pOPEN #PATH,"test1";READ ( open the file for reading.
OFOR T=0 TO 9
OSEEK #PATH,LENGTH*T ( find the beginning of each file.
OGET #PATH,LINE ( get a line from the file.
OPRINT LINE t display the line.
ONEXT T
OCLOSE #PATH ( close the file.
REND
ODATA "This is test line #1"
ODATA "This is test line #2"
ODATA "This is test line x3"
ODATA "This is test line x4"
ODATA "This is test line #5"
ODATA "This is test line x6"
ODATA "This is test line x7"
ODATA "This is test line x8"
ODATA "This is test line x9"
ODATA "This is test line x10"
This procedure creates a file named Testl. The variable named
Length stores the length of each line in the file (25 characters).
The string variable Null, is a string of 25 space characters. The
variable Line contains the data to store in each element (record)
in the file. The variable Path stores the path number of the file.
Next, the procedure contains an ON ERROR routine that deletes
the file Testl, if it already exists. Without this routine, the procedure produces an error if you execute it more than once.
Next, the routine uses CREATE to open the file Testl. The line
SEEK # P A T H , LENGTH * T
sets the file pointer to the proper location to store the next line. Because Length is established as 25,
the file lines are stored at 0, 25, 50, 75, and so on.
After the routine initializes storage space, it begins to store
data by reading the procedure data lines one at a time, seeking
the proper file location, and putting the data into the file. After
storing all 10 lines, it closes the file.
8-7
BASIC09 Reference
The last part of the routine opens the new file, uses the same
SEEK routine to position the file pointer, and reads the lines
back, one at a time, to confirm that the store routine is
successful.
The next short routine shows how you can use a procedure to
read any line you select in the file, without reading any preceding lines:
PROCEDURE randomread
ODIM LENGTH:BYTE
pDIM LINE;STRING(251
pDIM SEEKLINE;HYTE
ODIM PATH;HYTE
pLENGTH=25
pOPEN #PATH,"test1":READ ( open the file for reading.
DINPUT "Line number to display",SEEKLINE (* type a line to get.
pEXITIF SEEKLINE)10 OR SEEKLINE<1 THEN (* test if record
i5
valid.
DENDEXIT ( exit loop if not.
pSEEK #PATH,(SEEKLINE-1)*LENGTH ( find the requested record.
pGET #PATH,LINE ( read the record.
pPRINT LINE ( display the record.
DPRINT
pENDL00P
pPRINT "That's all " ( end session.
pCLOSE #PATH ( close path.
pEND
The procedure asks for the record number of the line to display.
When you type the number (1-10) and press
ENTER
, SEEK moves
the file pointer to the beginning of the record you want, GET
reads it into the variable Line, and PRINT displays it. The calculation
( S E E K L I N E -1 ) * L E N G T H
determines the beginning of
the line you want. If you type a number outside the range of
lines contained in the file (1-10), the procedure drops down to
Line 100 and ends.
By changing this procedure slightly, you can replace any line in
the procedure with another line. The altered procedure below
demonstrates this:
8-8
Disk Files l 8
PROCEDURE random-replace
ODIM LENGTH:BYTE
ODIM LINE:STRINGf251
ODIM SEEKLINE:BYTE
ODIM PATH:BYTE
OLENGTH=25
OOPEN #PATH,"test1":UPDATE(* open the file.
OLOOP
DINPUT "Line number to display ...",SEEKLINE (* type record to find.
DEXITIF SEEKLINE)10 OR SEEKLINE<1 THEN (* test if valid number.
OENDEXIT ( exit loop if not
OSEEK #PATH,(SEEKLINE-1)*LENGTH ( find the requested record.
OGET #PATH,LINE
(* get the data.
OPRINT LINE (* print the record.
OPR I NT
OINPUT "Type new line... ",LINE (* type a new line.
OSEEK #PATH,(SEEKLINE-1)*LENGTH (* find beginning of the record.
OPUT #PATH,LINE
(* store the new line.
OENDLOOP (* do it all again.
OPRINT "That's all
" (* terminate procedure.
OCLOSE #PATH
(* close path.
REND
This time, the file is opened in the UPDATE mode to allow both
reading and writing. You type the line you want to display. A
prompt then asks you to type a new line. The procedure
exchanges the new line for the original line, and stores it back in
the file.
Using Arrays With Random Access Files
BASIC09's random access filing system is even more impressive
when used with data structures, such as arrays. Instead of using
a loop to store the 10 lines of the Random-replace procedure,
you could store them all at once, into one record, using an array.
The following procedure illustrates this:
8-9
BASIC09 Reference
PROCEDURE arraywrite
ODIM LENGTH:HYTE
ODIM LINE:STRINGt25l
ODIM RECORD(10):STRINGf25l
ODIM PATH:BYTE
OLENGTH=25
DON ERROR GOTO 10
DELETE "testl"
1000N ERROR
(* delete Testl if it exists,
OCREATE #PATH,"testl":WRITE ( create Test1,
OHASE 0
OFOR T=0 TO 9
DREAD RECORD(T)
ONEXT T
OSEEK #PATH,O
OPUT #PATH,RECORD
OCLOSE #PATH
DOPEN xPATH,"testl":READ
OFOR T=0 TO 9
OSEEK xPATH,LENGTH*T
OGET xPATH,LINE
OPRINT LINE
ONEXT T
OCLOSE PATH
REND
ODATA "This is test line 01"
ODATA "This is test line x2"
ODATA "This is test line x3"
ODATA "This is test line x4"
ODATA "This is test line 6"
ODATA "This is test line x6"
ODATA "This is test line
01
ODATA "This is test line x8"
ODATA "This is test line #9"
ODATA "This is test line x10"
Read data lines into RECORD array,
set pointer to beginning of file,
Store the entire array into file,
close path to file,
open the file to read,
find each element,
read an element,
print the element,
8-10
Disk Files / 8
This procedure reads the 10 lines into an array named Records.
Then it places the entire array in the Testl file, using one PUT
statement. To show that the structure of the file is still the
same, the original FOR/NEXT loop reads the lines, one at a
time, and displays them.
Notice that, because you need to write only one element, you can
set the file pointer to 0
( s
E E K # P A T H , 0).
You can rewind a file
pointer (set it to 0) at any time in this manner.
You could save additional programming space by also reading the
10 lines back into memory as an array. The following procedure
uses a new array, Readlines, to call the file back into memory,
and displays the lines.
PROCEDURE arrayread
DHASE 1
DDIM READLiNES(10);STRINGL25l
DDIM PATH;HYTE
DOPEN xPRTH,"te5t1";READ (~ open file,
DGET xPATH,READLINES (~ read file into array,
` DCLOSE #PATH
DFOR T=1 TO 9
DPRINT READLINES(T) (~ print each element of the array,
DNEXT T
[]END
Using Complex Data Structures
In the previous section, you stored and retrieved elements of an
array that were all the same size, 25 characters. Often you need
to store elements of varying sizes, such as when you create a
data base program with several fields in one record.
The following examples create a simple inventory system that
requires a random access file having 100 records. Each record
includes the name of the item (a 25-byte string), the item's list
price and cost (both real numbers), and the quantity on hand (an
integer).
8-11
BASIC09 Reference
First, you use the TYPE command to define a new data type
that describes such a record. For example:
TYPE INV-ITEM= NAME: STRINGf25J;LIST,COST:REAL;
QTY: INTEGER
Although this statement describes a new record type called
Inv-item, it does not assign variable storage for the record. The
next step is to create two data structures: an array of 100 records of type Inv-item named Inv-array and a working record
named
Work-rec. The following lines do this:
DIM INV_ARRAY(1 00): INV-ITEM
DIM WORK-REC : I NV-ITEM
To determine the number of bytes assigned for each type, you
can use BASIC09's SIZE command. SIZE returns the number of
bytes assigned to any variable, array, or complex data structure.
For example, the command line
SIZE (W 0 R K I -R E C)
returns the
number 37. The command
S I Z E ( I N V-A R R A Y ) returns
the number
3700.
You can use SIZE with SEEK to position a file pointer to a specific record's address.
The following procedure creates a file called Inventory and
immediately initializes it with zeroes and nulls strings. Five
INPUT lines then ask you for a record number and the data to
store in each field of the record. You can fill any record you
choose, from 1 through 100.
When one record is complete, the procedure uses PUT to store
the record. Then, it asks you for a new record number. If you
wish to quit, enter a number either larger than 100 or smaller
than 1.
PROCEDURE inventory
OREM Create a data type consisting of a 25-character name field,
OREM a real list price field, a real cost field, and an integer
OREM quantity field.
OTYPE INV-ITEM=NAME:STRINGt25l; LIST,COST:REAL; QTY:INTEGER.
pDIM INV-ARRAY(100):INV_ITEM (* dimension an array using new type,
8-12
Disk Files l 8
pDIM WORK-REC; INV-ITEM
OREM (* dimension a working variable of the new type.
,,,~-~ pDIM PATH;HYTE
DON ERROR GOTO 1e
pDELETE "inventory"
ICON ERROR
pCREATE OPATH,"inventory" ( create a file named Inventory,
OWORK-REC.NAME=" ( set all data elements to null or 8.
OWORK-REC,LIST=0
OWORK-REC. COST=0
OWORK-REC.QTY=8
OFOR N=1 TO 188
OPUT #PATH,WORK-REC
ONEXT N
pL00P
OINPUT "Record number? ",RNUM ( enter number of record to write.
OF RNUM<1 OR RNUM>188 THEN ( check if number is valid.
pPRINT
OPRINT "End of Session" ( if not, end session.
OPRINT
OCLOSE #PATH
OEND
DENDIF
OINPUT "Item name? ",WORK-REC.NAME ( type data for record.
OINPUT "List price? ",WORK-REC.LIST
OINPUT "Cost price? " WORK-REC. COST
OINPUT "Quantity? ",WORK-REC.QTY
OSEEK #PATH,(RNUM-1)*SIZE(WORK-REC) (* find record.
pPUT #PATH,WORK-REC ( write record to file.
OENDLOOP
Notice that the INPUT statements reference each field separately, but the PUT statement references the record as a whole.
The next procedure lets you read any record in your Inventory
file, and displays that record. If you ask for a record you have not
yet filled with meaningful data, the display consists of a null
string and zeroes.
PROCEDURE readinv
OTYPE INV-ITEMzNAME;STRING(25I; LIST,COST;REAL; QTY;INTEGER
ODIM WORK-REC; INV-ITEM
8-13
BASIC09 Reference
[]DIM PATH:BYTE
[]OPEN OPATH,"INVENTORY";READ
DLOOP ,,
[]INPUT "Record number to display? ",RNUM
OF RNUM<1 OR RNUM>180 THEN
OPRINT "End of Session"
[]PRINT
OCLOSE #PATH
[]END
DENDIF
[]SEEK #PATH, (RNUM-1)*SIZE(WORK-REC)
[]GET #PATH,WORK-REC
[]PRINT "x","Item","List Price","Cost Price","Quantity"
pPRINT ·'--____--______-___________-____________________________________________
[]PRINT RNUM,WORK-REC.NAME,WORK-REC.LIST,WORK-REC.COST,WORK-REC.QTY
pPR I NT
pENDLOOP
OEND
This procedure accesses the file one record at a time. It is not
necessary to do so. You can read the entire file into memory at
once by dimensioning an inventory array and getting the whole
file into it:
[]TYPE INV-ITEM-NAME;STRINGf251; LIST,COST:REAL; QTY:INTEGER
ODIM INV-ARRAY(108);INV-ITEM
[]SEEK #PATH, 6 (rewind the file*)
[]GET #PATH, INV-.ARRAY
The examples in this section are simple, yet they illustrate the
combined power of BASIC09 complex data structures and the
random access file statements. They show that a single GET or
PUT statement can move any amount of data, organized in any
way you want. Other advantages are of using complex data structures are:
The procedures are self-documenting. You can see easily
what a procedure does because its structures can have
descriptive names.
· Execution is extremely fast.
Procedures are simple and usually require fewer statements to perform I/O functions than other BASICS.
8-14
Disk Files / 8
The procedures are versatile. By creating appropriate
data structures, you can read or write almost any kind
of data from any file, including files created by other programs or languages.
8-15