You are on page 1of 35

TutorialSequentialTextFile Edit 12

Creating, Reading, and Appending Sequential Text Files


Sequential File Commands and Operations
Creating a Sequential Text File
Reading a Sequential Text File
LOF()
EOF()
Counting Data
Line Input
Comma Deliminators
Inputto$()
Input$()
Appending a Sequential Text File
Common Errors
A sequential text file is a file where characters are placed in sequence, one ri
ght after the other. Sequential text files have no set format other than a begin
ning and an end. A sequential text file can only be read starting from the begin
ning of the file. Each character is printed as an ASCII text character. The most
common example of a sequential text file is the .txt file saved with Notepad or
any other editing tool. Run the following code to see the output of simple PRIN
T statements.
PRINT "Tom Sawyer"
PRINT "Huckleberry Finn"
PRINT "The Prince and the Pauper"
PRINT "A Connecticut Yankee in King Arthur's Court"
PRINT "A Tramp Abroad"
Output
Tom Sawyer
Huckleberry Finn
The Prince and the Pauper
A Connecticut Yankee in King Arthur's Court
A Tramp Abroad
When the program is closed, the text is gone. A sequential text file allows that
text to be saved to file.
Sequential File Commands and Operations
There are three basic operations in sequential text files.
Creating and writing data into a file
Reading data from an existing file
Appending or adding data into an existing file
Basic File commands and functions include
OPEN- used to access the file
OUTPUT - used to write data to the file
INPUT - used to read data from the file
LOF() - length of file
EOF() - end of file
CLOSE - used to close the file
Special File commands and functions include
LINE INPUT - extract a full line of data
INPUTTO$ - extract data up to the assigned deliminator
INPUT$() - extract a number of bytes of data starting from the beginning
Creating a Sequential Text File

Sequential text files are always created in this fashion


OPEN "FileName.ext" for OUTPUT as #1
The commands OPEN and OUTPUT are case-insensitive. "FileName.ext" can be any nam
e and extension you choose. The file name can also be a variable.
fName$ = "FileName.ext"
OPEN fName$ for OUTPUT as #1
#1 is also an arbitrary name. #a, #MyTextFile, #123xyz, or any other combination
of numbers and letters will work as well.
Once the file has been opened, data can be written into that file. The data can
be literal
PRINT #1, "Tom Sawyer"
or a variable
t$ = "Tom Sawyer"
PRINT #1, t$
Finally, when all the data has been written, the file must be closed.
CLOSE #1
The following code creates the text file "TwainNovels.txt"
OPEN "TwainNovels.txt" for OUTPUT as #1
PRINT #1, "Tom Sawyer"
PRINT #1, "Huckleberry Finn"
PRINT #1, "The Prince and the Pauper"
PRINT #1, "A Connecticut Yankee in King Arthur's Court"
PRINT #1, "A Tramp Abroad"
CLOSE #1
END
Run the above code before proceeding with this tutorial.
Reading a Sequential Text File
Reading a sequential text file also requires the file be OPENed, but for INPUT.
OPEN "TwainNovels.txt" for INPUT as #1
INPUT #1, n1$
INPUT #1, n2$
INPUT #1, n3$
INPUT #1, n4$
INPUT #1, n5$
CLOSE #1
PRINT n1$
PRINT n2$
PRINT n3$
PRINT n4$
PRINT n5$
END
Output
Tom Sawyer
Huckleberry Finn
The Prince and the Pauper
A Connecticut Yankee in King Arthur's Court
A Tramp Abroad
LOF - Length of File
The length, or size, of a sequential text file is determined by the number of ch
aracters it contains. Each character occupies one byte. That size is obtained wi
th the LOF() function. The file must first be opened.
OPEN "TwainNovels.txt" for INPUT as #1
Print "LOF = ";LOF(#1)
INPUT #1, n1$
INPUT #1, n2$

INPUT #1, n3$


INPUT #1, n4$
INPUT #1, n5$
CLOSE #1
nChars = 0
PRINT n1$
nChars = nChars + Len(n1$)
PRINT n2$
nChars = nChars + Len(n2$)
PRINT n3$
nChars = nChars + Len(n3$)
PRINT n4$
nChars = nChars + Len(n4$)
PRINT n5$
nChars = nChars + Len(n5$)
Print "nChars = ";nChars
END
Output
LOF = 118
Tom Sawyer
Huckleberry Finn
The Prince and the Pauper
A Connecticut Yankee in King Arthur's Court
A Tramp Abroad
nChars = 108
There is an inequality here. LOF equals 118 and nChars equals 108. Where are the
other 10 characters?
End-of-Line Markers
Windows marks the end of each record with a carriage return / line feed sequence
. At the end of each novel title are these two end-of-line marker characters: Ch
r$(13) and Chr$(10), two extra characters for each of the five lines. That's ten
extra characters.
Sequential text files save all characters in their corresponding ASCII format an
d display as they do on the keyboard. The letter 'a' is seen as 'a', the number
1 is seen as '1'. Some characters, Control Key, Escape, Enter, to name a few, ar
e invisible characters. They are imbedded in the sequential text file but cannot
be seen. The LOF() function incudes these characters when counting the size of
the file. The INPUTed variables do not include these end-of-line markers.
EOF - End of File
Liberty BASIC uses a special End of File function, EOF(), to determine if the En
d of File has been reached. If the End of File has been reached, EOF returns 1 (
True). If the End of File has not been reached, EOF returns 0 (False). EOF is us
eful for retrieving information when the file contains an unknown amount of data
.
OPEN "TwainNovels.txt" for INPUT as #1
WHILE EOF(#1) = 0
INPUT #1, n$
PRINT n$
WEND
CLOSE #1
END
Output
Tom Sawyer
Huckleberry Finn
The Prince and the Pauper
A Connecticut Yankee in King Arthur's Court

A Tramp Abroad
WHILE EOF(#1) = 0 translates to While the End of File is false (hasn't been reac
hed)
This could also be coded as
OPEN "TwainNovels.txt" for INPUT as #1
WHILE EOF(#1) <> 1
INPUT #1, n$
PRINT n$
WEND
CLOSE #1
END
Output
Tom Sawyer
Huckleberry Finn
The Prince and the Pauper
A Connecticut Yankee in King Arthur's Court
A Tramp Abroad
WHILE EOF(#1) <> 1 translates to While the End of File is not true (hasn't been
reached)
A third way is to use the NOT() operator:
WHILE NOT(EOF(#1)) translates to While not at the End of File
In either case, the code continues to input data until the End of File has been
reached.
Counting the Data
Sometimes the number of data needs to be known. This is especially true if an ar
ray is used to capture the data. If a sequential text file contains an unknown n
umber of data, use a counter to determine that exact number. Increase the counte
r by one with each data read.
ct = 0
OPEN "TwainNovels.txt" for INPUT as #1
DO
ct = ct + 1
INPUT #1, n$
PRINT ct, n$
LOOP WHILE EOF(#1) = 0
CLOSE #1
PRINT "There are ";ct;" items of data in this text file."
END
Output
1
Tom Sawyer
2
Huckleberry Finn
3
The Prince and the Pauper
4
A Connecticut Yankee in King Arthur's Court
5
A Tramp Abroad
There are 5 items of data in this text file.
Once the number of data is known, DIM the array, OPEN the file again, and INPUT
the data into the array. Be sure to CLOSE the file first so the INPUT begans wit
h the first data.
nNovels = 5 ' The number obtained by the counter
Dim TwainNovel$(nNovels)
OPEN "TwainNovels.txt" for INPUT as #1
FOR i = 1 to nNovels
INPUT #1, TwainNovel$(i)
NEXT i
CLOSE #1

FOR i = 1 to nNovels
PRINT TwainNovel$(i)
NEXT i
END
Output
Tom Sawyer
Huckleberry Finn
The Prince and the Pauper
A Connecticut Yankee in King Arthur's Court
A Tramp Abroad
Recapping
To read a known number of data from a sequential file
OPEN the file
use a FOR NEXT loop to INPUT data
CLOSE the file
To read an unknown number of data from a sequential text file
OPEN the file
use a WHILE WEND to INPUT data until the EOF is reached
CLOSE the file
To count the number of data in a sequential text file
OPEN the file
use a counter
use a DO LOOP to INPUT data until EOF is reached
CLOSE the file
OPEN the file again
use a FOR NEXT loop to INPUT data
CLOSE the file again
Comma Deliminators
It's not just End of Line markers that separate data; a comma will also separate
data. Deliminator is another word for separator. Run this code to create a new
file.
OPEN "SevenDwarves.txt" for OUTPUT as #1
PRINT #1, "Happy, Sleepy, Bashful, Grumpy, Sneezey, Doc, Dopey"
CLOSE #1
END
Next, read the file.
OPEN "SevenDwarves.txt" for INPUT as #1
INPUT #1, dwarf$
PRINT dwarf$
CLOSE #1
END
Output
Happy
Where are the rest of the dwarves? INPUT will only grab data up to, but not incl
uding, the comma. Use either a FOR NEXT loop if you know the number of data to I
NPUT
' Number of data is known
OPEN "SevenDwarves.txt" for INPUT as #1
FOR i = 1 to 7
INPUT #1, dwarf$
PRINT dwarf$
NEXT i
CLOSE #1
END
Output

Happy
Sleepy
Bashful
Grumpy
Sneezey
Doc
Dopey
or use a WHILE WEND loop if you don't.
' Number of data unknown
OPEN "SevenDwarves.txt" for INPUT as #1
WHILE EOF(#1) = 0
INPUT #1, dwarf$
PRINT dwarf$
WEND
CLOSE #1
END
Output
Happy
Sleepy
Bashful
Grumpy
Sneezey
Doc
Dopey
It isn't always desirable to deliminate data with commas. To place the entire li
ne of data, including any commas, in a variable, use LINE INPUT.
Line Input
LINE INPUT will extract the full line of data, up to the End of Line markers. LI
NE INPUT ignores commas as deliminators.
OPEN "SevenDwarves.txt" for INPUT as #1
LINE INPUT #1, dwarf$
PRINT dwarf$
CLOSE #1
END
Output
Happy, Sleepy, Bashful, Grumpy, Sneezey, Doc, Dopey
Assigning a Unique Deliminator
Actually, any character can be assigned as the deliminator. The assigned delimin
ator is substituted for the usual comma deliminator. The command for assigning a
unique deliminator is INPUTTO$(). The following code separates the SevenDwarves
data at the r.
OPEN "SevenDwarves.txt" for INPUT as #1
For i = 1 to 2
n$ = INPUTTO$(#1, "r")
PRINT n$
Next i
CLOSE #1
END
Output
Happy, Sleepy, Bashful, G
umpy, Sneezey, Doc, Dopey
The unique deliminator, in this case the r, is lost, just as the comma is lost w
ith INPUT. Note, also, that assigning a unique deliminator does not prevent the
usual End of Line marker, Chr$(13);Chr$(10), from forcing a new line.
OPEN "TwainNovels.txt" for INPUT as #1
WHILE EOF(#1) = 0

n$ = INPUTTO$(#1, "r")
PRINT n$
WEND
CLOSE #1
END
Output
Tom Sawye
Hucklebe
y Finn
The P
ince and the Paupe
A Connecticut Yankee in King A
thu
's Cou
t
A T
amp Ab
oad
The blank lines are the result of the Chr$(13);Chr$(10) End of Line markers.
Placing the Entire Contents of the File into One String Variable
Partial or full contents of the file can be read using the INPUT$() function. Co
ntents of the file, from the beginning to a defined number of bytes can be place
d in a string variable.
OPEN "TwainNovels.txt" for INPUT as #1
n$ = INPUT$(#1, 15)
CLOSE #1
PRINT n$
END
Output
Tom Sawyer
Huc
Only 13 characters are visible. The other 2 bytes are the invisible characters,
Chr$(13) and Chr$(10), that force a new line at the end of Tom Sawyer.
OPEN "TwainNovels.txt" for INPUT as #1
n$ = INPUT$(#1, 15)
CLOSE #1
PRINT n$
FOR i = 1 to LEN(n$)
PRINT i, MID$(n$, i, 1), ASC(MID$(n$, i, 1))
NEXT i
END
Output
Tom Sawyer
Huc
1
T
84
2
o
111
3
m
109
4
32
5
S
83
6
a
97
7
w
119
8
y
121
9
e
101
10
r
114
11
13

12
10
13
H
72
14
u
117
15
c
99
The entire contents of the file may be read and stored in one string variable. T
his may be useful for text files containing narrative paragraphs, or for later p
arsing of unknown data. Whatever the reason, the entire contents is defined with
the LOF() function.
OPEN "TwainNovels.txt" for INPUT as #1
n$ = INPUT$(#1, LOF(#1))
CLOSE #1
PRINT n$
END
Output
Tom Sawyer
Huckleberry Finn
The Prince and the Pauper
A Connecticut Yankee in King Arthur's Court
A Tramp Abroad
Recapping
Read a simple sequential text file with the INPUT command
Read an entire line of data, including commas, with the LINE INPUT command
Read the entire file as one string variable with the INPUT$() and LOF() function
s
Appending a Sequential Text File
PRINTing to a sequential text file that's been OPENed for OUTPUT will not place
the new text at the end of the file. It will, in fact, overwrite the file.
OPEN "TwainNovels.txt" for OUTPUT as #1
PRINT #1, "The Guilded Age"
CLOSE #1
OPEN "TwainNovels.txt" for INPUT as #1
n$ = INPUT$(#1, LOF(#1))
CLOSE #1
PRINT n$
END
Ouput
The Guilded Age
"The Guilded Age" was not added to the contents of TwainNovels.txt, but, instead
, replaced the contents of TwainNovels.txt. To add information to an existing se
quential text file, use the APPEND command. First, rebuild the original TwainNov
els.txt file.
OPEN "TwainNovels.txt" for OUTPUT as #1
PRINT #1, "Tom Sawyer"
PRINT #1, "Huckleberry Finn"
PRINT #1, "The Prince and the Pauper"
PRINT #1, "A Connecticut Yankee in King Arthur's Court"
PRINT #1, "A Tramp Abroad"
CLOSE #1
Read the contents of the file.
OPEN "TwainNovels.txt" for INPUT as #1
FOR i = 1 to 5
INPUT #1, n$
PRINT n$
NEXT i
CLOSE #1
END
Output

Tom Sawyer
Huckleberry Finn
The Prince and the Pauper
A Connecticut Yankee in King Arthur's Court
A Tramp Abroad
Now, OPEN the file for APPEND to add more content.
OPEN "TwainNovels.txt" for APPEND as #1
PRINT #1, "The Guilded Age"
CLOSE #1
OPEN "TwainNovels.txt" for INPUT as #1
FOR i = 1 to 6
INPUT #1, n$
PRINT n$
NEXT i
CLOSE #1
END
Output
Tom Sawyer
Huckleberry Finn
The Prince and the Pauper
A Connecticut Yankee in King Arthur's Court
A Tramp Abroad
The Guilded Age
Recapping
To create a new sequential text file, use OPEN for OUTPUT
To read a sequential text file, use OPEN for INPUT
To add content to an existing text file, use OPEN for APPEND
Common Errors with Sequential Text File Programming
Liberty BASIC will halt execution with an error message if any problems are enco
untered during opening, reading, and writing sequential text files. The followin
g code will present no error if TwainNovels.txt remains present in the relevant
Liberty BASIC directory.
ON ERROR GOTO [ErrorDeBug]
OPEN "TwainNovels.txt" for INPUT as #1
FOR i = 1 to 5
INPUT #1, n$
PRINT n$
NEXT i
CLOSE #1
END
[ErrorDeBug]
PRINT Err
END
Errors will occur if the file can't be found, can't be opened, can't be written
to, or attempts are made to extract data that isn't there.
File Doesn't Exist (Err = 62)
can happen when the file doesn't exist, when the file name is simply misspelled
or when the path to that file is wrong. This is a common occurance when a path i
s hardcoded rather than relevant.
Example
' TwainNovels is misspelled TwainNovel
ON ERROR GOTO [ErrorDeBug]
OPEN "TwainNovel.txt" for INPUT as #1
FOR i = 1 to 5
INPUT #1, n$
PRINT n$

NEXT i
CLOSE #1
END
[ErrorDeBug]
PRINT Err
END
Liberty BASIC closes, giving the warning
Runtime Error: OS Error: The system cannot find the file specified.
(see error.log for more information)
File Already Open (Err = 0)
occurs when an attempt is made to OPEN a file that is already OPEN.
Example
' TwainNovels.txt is opened twice
ON ERROR GOTO [ErrorDeBug]
OPEN "TwainNovels.txt" for INPUT as #1
OPEN "TwainNovels.txt" for INPUT as #1
FOR i = 1 to 5
INPUT #1, n$
PRINT n$
NEXT i
CLOSE #1
END
[ErrorDeBug]
PRINT Err
END
Liberty BASIC first gives a notice that the program has ended and all open windo
ws and files have been closed
Please Note:
#1
These handles closed by Liberty BASIC.
Please add the appropriate CLOSE commands.
then a warning is given regarding the error.
Warning
Runtime Error: Handle #1 already in use
(see error.log for more information)
Note that there is not an Error number associated with this error.
Input Past End of File (Err = 62)
occurs when attempts are made to extract more data than what the file holds.
Example
' Run out of data
ON ERROR GOTO [ErrorDeBug]
OPEN "TwainNovels.txt" for INPUT as #1
FOR i = 1 to 20
INPUT #1, n$
PRINT n$
NEXT i
CLOSE #1
END
[ErrorDeBug]
PRINT Err
END
The usual Liberty BASIC notice that the program has ended and all open windows a
nd files have been closed is given
Please Note:
#1
These handles closed by Liberty BASIC.
Please add the appropriate CLOSE commands.

followed by a warning regarding the error.


Warning
Runtime Error: Input past end of file: #1
(see error.log for more information)
Attempting to INPUT data from a file OPENed for OUTPUT (Err = 62)
Liberty BASIC creates a new file whenever that file is OPENed for OUTPUT. Attemp
ts to then INPUT data will result in the same error as attempting to extract mor
e data than what the file holds.
Example
' Input from file opened for Output
ON ERROR GOTO [ErrorDeBug]
OPEN "TwainNovel.txt" for OUTPUT as #1
FOR i = 1 to 5
INPUT #1, n$
PRINT n$
NEXT i
CLOSE #1
END
[ErrorDeBug]
PRINT Err
END
Liberty BASIC will close all windows, files, and the program itself
Please Note:
#1
These handles closed by Liberty BASIC.
Please add the appropriate CLOSE commands.
and then give the error warning.
Warning
Runtime Error: Input past end of file: #1
(see error.log for more information)
Writing to a File OPENed for INPUT (Err = 0)
results in a serious error, from which Liberty BASIC cannot recover. It is NOT R
ECOMMENDED that you run the following code, as you WILL have to use the TASK MAN
AGER to recover and end the program.
Example
ON ERROR GOTO [ErrorDeBug]
OPEN "TwainNovels.txt" for INPUT as #1
PRINT #1, "A Guilded Age"
CLOSE #1
END
[ErrorDeBug]
PRINT Err
END
results in this warning
Runtime Error: FileSystemAccessDenied
(see error.log for more information)
Again, you WILL have to use the TASK MANAGER to recover from this error.
For more information on errors and debugging your program, see Fast FAQ.
Advantages and Disadvantages of a Sequential Text File
Sequential text files can be used to store any type of data. The structure is si
mple and the contents are easily obtainable. Sequential text files must be read
starting from the beginning. As files increase in size, it may become time consu
ming to search for information toward the end of the file. Any change to the fil
e necessitates a rewriting of the entire file to disk. Still, for small amounts
of data, sequential text files offer the most economical option.

https://lbpe.wikispaces.com/TutorialSequentialTextFile#sf04
==========================================
Sequential Files
Sequential files are opened with the OPEN statement. When they are no longer ne
eded, or when the program ends, they must be closed with the CLOSE statement.
Sequential file access allows data to be read from a file or written to a file f
rom beginning to end. It is not possible to read data starting in the middle of
the file, nor is it possible to write data to the file starting in the middle u
sing sequential methods.
Data is read from a file opened for INPUT starting at the beginning of the file.
Each subsequent input statement reads the next piece of data in the file. Data
is written to a file opened for OUTPUT with a PRINT statement starting at the b
eginning of the file, and each subsequent PRINT statement writes data to the end
of the open file. When a file is opened for APPEND, each PRINT statement write
s data to the end of the open file.
Sequential File Access
INPUT
Files opened for INPUT can be read from. They cannot be written to. A file ope
ned for INPUT must exist on disk, or the program will halt with an error. See T
esting For File Existence.
The INPUT statement reads a piece of data up to the next comma or carriage retur
n. The LINE INPUT statement reads a piece of data that contains commas that are
not delimiters, and stops reading data at the next carriage return. The INPUT$
statement reads data of a specified length from a file. the INPUTTO$ statement
reads data up to a specified delimiter. Here is an illustration of the differe
nces between the various forms of INPUT statements.
Example Program:
'create a sample file
open "test.txt" for output as #1
print #1, "123 Sesame Street, New York, NY"
close #1
'INPUT
open "test.txt" for input as #1
INPUT #1, txt$
print "INPUT item is: ";txt$
close #1
'LINE INPUT
open "test.txt" for input as #1
LINE INPUT #1, txt$
print "LINE INPUT item is: ";txt$
close #1
'INPUT$
open "test.txt" for input as #1
txt$ = INPUT$(#1, 10) 'read 10 characters
print "INPUT$ item is: ";txt$
close #1
'INPUTTO$

open "test.txt" for input as #1


txt$ = INPUTTO$(#1, " ") 'use a blank space as delimiter
print "INPUTTO$ item is: ";txt$
close #1

Produces:
INPUT item is: 123 Sesame Street
LINE INPUT item is: 123 Sesame Street, New York, NY
INPUT$ item is: 123 Sesame
INPUTTO$ item is: 123
INPUT Multiple Items
Here is a short program which opens a text file and reads a line at a time, prin
ting each line to the mainwin.
filedialog "Open ","*.txt", file$
if file$="" then end
open file$ for input as #1
while eof(#1) = 0
line input #1, text$
print text$
wend
close #1
'print a notice that the end of file is reached:
print:print:print "EOF"

OUTPUT
Files opened for OUTPUT can be written to sequentially. If a file opened for OU
TPUT does not exist, it will be created. If the file does exist on disk, the pr
evious contents will be overwritten, and therefore lost. Care should be taken w
hen opening files for OUTPUT so that critical data is not accidentally erased. S
ee Testing For File Existence.
Data is written to a file opened for OUTPUT with a PRINT statement. A line deli
miter or carriage return is written to the file with each PRINT statement. The c
arriage return may be suppressed by ending the line of code with a semi-colon.
Example Program:
'create a sample file
open "test.txt" for output as #1
'write some data with line delimiters
print #1, "line one "
print #1, "line two "
'write some data without line delimiters
print #1, "item three ";
print #1, "item four ";
'more
print
print
close

data with line delimiters added


#1, "item five"
#1, "done"
#1

'INPUT to see what we wrote


open "test.txt" for input as #1
txt$ = input$(#1, lof(#1))
print "Contents of file: "
print
print txt$
close #1
Contents of file:
line one
line two
item three item four item five
done
APPEND
Files opened for APPEND can be written to sequentially. If a file opened for AP
PEND does not exist, it will be created. If the file does exist on disk, any da
ta written to the file with a PRINT statement will be added to the end. Writing
data to the file works in the same way when a file is opened for APPEND as when
it is opened for OUTPUT, but rather than overwriting data contained in the file
, the new data is appended to the end of the file, but does not overwrite data p
reviously written to the file the last time it was opened as open for OUTPUT doe
s.
.
Example Program:
open "test.txt" for append as #1
'write some data with line delimiters
print #1, "line one "
print #1, "line two "
'write some data without line delimiters
print #1, "item three ";
print #1, "item four ";
'more
print
print
close

data with line delimiters added


#1, "item five"
#1, "done"
#1

File Copy
A file may be copied using sequential file operations. The file to be copied is
opened for INPUT. The file that is to be a copy is then opened for OUTPUT. The
contents of the original file are retrieved with the INPUT$ statement and writt
en to the copy with the PRINT statement. Both files are then closed. Here is a
n example:
open "mybytes.bin" for input as #original
open "copybyte.bin" for output as #copy
print #copy, input$(#original, lof(#original));
close #original
close #copy
end
http://www.libertybasicuniversity.com/lb4help/Sequential_Files.htm

==========================================
DISK FILE HANDLING IN LIBERTY BASIC By Dean Hodgson
copyright (c) 1999 dhodgso-@nexus.edu.au
Part 1 - Basic principals of disk file handling
Part 2 - LB native disk file commands
This series of articles is intended to provide you with a broad understanding of
disk file handling techniques in Liberty Basic. There are many ways to achieve
this, and the method you employ depends on what you need to do. It is not the in
tention of these articles to provide you with ready-made code you can cut and pa
ste directly into your programs. Rather, the concentration will be on the concep
ts behind disk file handling with short examples given.
Note: the statements DefaultDir$, Drive$, FILEDIALOG, FILES, KILL, MKDIR, NAME a
nd RMDIR are not covered in these articles.
Part 1 - Basic Principles of Disk File Handling
There are three main types of files: Sequential, Random Access and Binary.
Sequential files contain data that must be read from the start to the finish eve
ry time. These files typically contain text information stored line-by-line. The
whole file is read into variables every time it is used. The data is updated in
memory then the entire file is written back to the disk. Sequential files are g
reat when the amount of data is limited and/or only its order is important.
In Random Access files, individual records of data are read into the computer's
memory, with usually only one record being operated on at a time. Random files c
an be very large, much larger than the memory capacity of the computer. Data can
be read from or written to any spot in the file at any time. Random files are t
he 'backbone' of most database applications and are essential when sharing files
in network situations.
A third type of file is a Binary file. In this type, the file may or may not hav
e a particular record structure like a Random file and/or it may or may not be s
equential. Executable programs are Binary files. Liberty Basic only has limited
Binary file handling capability. However, Binary files can be accessed via API a
nd Deanslib functions.
A typical file procedure is:
Open the file
perform work on it
Close the file
It is a good idea not to keep files open any longer than necessary. Try not to o
pen a file at the start of your program and leave it open until the program fini
shes. This is especially important when sharing files on networks. And remember:
there are *always exceptions* to any rule anyone puts up.
Part 2 - LB Disk File Commands
Liberty Basic has a range of builtin or 'native' disk file commands for dealing
with both sequential and random access files. The statements are OPEN, CLOSE#, I
NPUT#, LINE INPUT#, PRINT#, INPUT$(), FIELD#, GET#, and PUT#.

SEQUENTIAL FILES
In a typical Sequential file, data is stored in a particular order but the lengt
h of each line can vary. In LB, a sequential file is opened either for Input or
Output:
This opens a sequential file for reading:
OPEN "filename" FOR INPUT AS #lbfilehandle
And this opens a file for writing:
OPEN "filename" FOR OUTPUT AS #lbfilehandle
"filename" is the name of your file. It can be up to 8 characters long followed
by a full stop, then an optional three-character extension. While Dos permits ot
her symbols, it is safest to stick to letters, numbers, the dash (-) and the und
erscore (_) symbols. Many punctuation symbols are interpreted by Dos as having s
pecial functions -- i.e. *, / \ ? ~ and so forth. The case of filename letters i
s not important as Dos automatically changes them internally to upper case.
The #lbfilehandle is a word, phrase or number you assign to the open file. This
is not the Windows file handle value, which is always a number and which is not
accessible for LB disk commands. It is also not a variable as such but a label d
esignating the handle you have assigned.
Trying to write to a file opened for Input generates an error message as does tr
ying to read a file opened for Output. Trying to open a file that does not exist
generates an error -- therefore if you are unsure it is worth testing to see if
a file exists first.
If a file already exists and you open it for output, that file will be cleared.
If the file does not exist, it is automatically created.
To close an open file use CLOSE #lbfilehandle.
To read data from an open Sequential file, use INPUT#, LINE INPUT# or INPUT$().
To write data use PRINT#.
Every line in a sequential file ends in two special characters:
a carriage-return (ASCII value of 13) then a line-feed (ASCII value of 10). LB's
commands automatically deal with these characters.
INPUT#lbfilehandle, var1$, var2
This statement inputs data from a sequential file opened for reading into the va
riables listed. The variables listed should match those used in the PRINT# state
ment to write the data. More than one variable can be listed, separated by comma
s. The INPUT# statement interprets commas as separating multiple variables on th
e same line of data. A carriage-return / line-feed combination signifies the end
of a line. INPUT# does not remove quotation marks around string variables, and
it skips the end of line characters.
LINE INPUT#lbfilehandle, var$
This statement reads an entire line of text into a string variable including all
characters. Reading is stopped when a carriage-return character is encountered.
Line-feed characters (ASCII value 10) are ignored. All other characters are rea
d, including commas and quotes.
PRINT#lbfilehandle, var1$ ; "," ; var2 ...
Print# is the main statement for writing sequential file data. The data is print
ed out to the file as text. If a semi-colon is omitted at the end of the PRINT#

statement, then a carriage-return/line-feed combination is also automatically wr


itten to mark the end of the line. If the statement ends in a semi-colon the end
-of-line marker is not written. Multiple variables can be listed, but each must
be separated by a ;","; combination and not just a simple comma. In LB, PRINT# d
oes not automatically put quotation marks around string variables. If you requir
e this for your data to be in "comma-delimited format" then you must add these t
o the variable before printing to the file -- i.e. A$=CHR$(34)+"Data"+CHR$(34).
Here is an example that creates and then reads a simple sequential file:
A$="abc" 'This is the data to be written
B$="def"
OPEN "TEMP.TXT" FOR OUTPUT AS #1 'The file Temp.Txt is opened for
' output using filehandle #1.
PRINT#1,A$;",";B$ 'The data is written to the line
CLOSE #1 'The file is closed
OPEN "TEMP.TXT" FOR INPUT AS #2 'Open the file for reading as #2
INPUT#2,A$,B$ 'Input the two variables
CLOSE #2 'Close the file
PRINT A$ 'Display the first variable
PRINT B$ 'Display the second variable
You should see abc for A$ and def for B$.
It is possible to add quote marks and commas to strings. However, using INPUT, L
B will recognise the comma as separating two variables.
A$=CHR$(34)+"123,456"+CHR$(34) 'This is the data to be written;
' note the comma and quotes!!
OPEN "TEMP.TXT" FOR OUTPUT AS #1 'The file Temp.Txt is opened for
' output using filehandle #1.
PRINT#1,A$ 'The data is written to the line
CLOSE #1 'The file is closed
OPEN "TEMP.TXT" FOR INPUT AS #2 'Open the file for reading as #2
INPUT#2,A$,B$ 'Input the two variables
CLOSE #2 'Close the file
PRINT A$ 'Display the first variable
PRINT B$ 'Display the second variable
In this example, A$ is read and displayed as "123. You'll see the quote mark at
the start. In many other Basics, 123,456 would have been read into A$ without qu
otes around it. This is because other Basics interpret the quotation marks as su
rrounding one whole string. LB does not do this! If it sees a comma within a str
ing, it stops reading into the variable.
The way to include commas is LINE INPUT#. If LINE INPUT#2,A$ were used instead o
f INPUT#2,A$,B$ above then the line "123,456" would be read, comma and all. A st
atement line LINE INPUT#2,A$,B$ reads two lines.
LB cannot read data from sequential files directly into an array. Instead, you h
ave to read into a simple variable then assign it to the array. If your file wer
e to contain the numbers from 1 to 10, one number per line, the following could
be used to read it:
DIM a$(10) 'allocate space for the array
OPEN "testfile.txt" FOR INPUT AS #1 'open the file
FOR counter=1 TO 10 'could through each line
LINE INPUT#1,temp$ 'input a line into a temp variable
a$(counter)=temp$ 'assign to the array element
NEXT counter 'end of the loop
CLOSE #1 'close the file

INPUT$(#handle,length)
There is one other input statement used in LB: INPUT$. This is a function that r
eads a specified number of characters into a string.
Example: a$=INPUT$(#1,50) would read 50 characters and assign them to a$. LB rea
ds the entire length specified regardless of the characters found! INPUT$ theref
ore transcends typical Sequential file statements and crosses over into the Bina
ry file, where there may be no set structure to the data.
END OF FILE DETECTION (EOF)
LB contains the function EOF(#filehandle) to detect when the end of a file has b
een reached. It is used in situations where the length of a file or its structur
e may not be known. EOF returns either a 0 (false) if the end of file has not be
en reached, or a -1 (true) if it has. In the example above, if we didn't know ho
w many numbers were in the file we could have used:
DIM a$(100) 'allocate space for the array
OPEN "testfile.txt" FOR INPUT AS #1 'open the file
counter=0 'initialize the counter
WHILE EOF(#1)=0 'loop until end of file found
LINE INPUT#1,temp$ 'input line into a temp variable
counter=counter+1 'increment the array pointer
a$(counter)=temp$ 'assign to the array element
WEND 'end of the loop
CLOSE #1 'close the file
In this case the variable 'counter' holds the number of entries read into the ar
ray.
LENGTH OF FILE (LOF)
The LOF(#filehandle) function returns the length of the open file in bytes. This
can be handy if you don't know the size of a file and want to read the whole th
ing using INPUT$ into one string variable:
OPEN "testfile.txt" FOR INPUT AS #1 'open the file
a$=INPUT$(#1,LOF(#1)) 'read whole file!
CLOSE #1 'close the file
Because LB strings can be megabytes in size, really large files can be read this
way. Parts of the file can then be examined using the MID$() string function. O
f course, because the whole file is within one string variable, you'll have to w
ork out your own way of breaking the parts up.
RANDOM ACCESS FILES
As stated previously, Random Access files use a "record" structure where the ent
ire file is divided into many records. Each record has the same length -- i.e. i
s the same number of bytes long. Each record is also subdivided into "fields", e
ach one of which is a given length. These are called fixed-length fields and rec
ords. Variable-length record files are possible but their complex structure is b
eyond the scope of these articles.
OPEN "filename" FOR RANDOM AS #lbfilehandle LEN=value
This variant of OPEN is used for opening random access files. RANDOM means it is
a random type file that can be either read or written to (using the same OPEN).
Note the LEN=statement at the end, which specifies the length of each record.
FIELD#filehandle,length AS var$ , ...

The fields in a random file are set up using the FIELD# statement. Each field ha
s a variable name and specified length. As an example, let's say that we want to
keep a simple database about a collection of books. The fields we want might in
clude Title, Authors, Series, Comments and the number of pages.
OPEN "books.dat" FOR RANDOM AS #1 LEN=425
FIELD#1,_
100 AS Title$,_
60 AS Authors$,_
60 AS Series$,_
200 AS Comments$,_
5 AS Pages
The value after LEN= is 100 + 60 + 60 + 200 + 5 or the total length of all the f
ields in FIELD#. Field# always follows the OPEN random file statement and must b
e used before GET# or PUT#. Note that for the numeric variable Pages, the space
necessary to store the number of digits in the largest number we expect to have
is reserved. LB stores numeric variables as text rather than in IEEE, BCD, MBF o
r other formats used by other languages. These format store numbers in native 'm
achine format'. An integer, for example, is a number between -32767 and +32767 a
nd is stored as only 2 bytes instead of 6. This avoids having to use MKI, CVI, e
tc. as is needed in Quick Basic.
PUT#filehandle,recordnumber
This statement writes the data held in the variables listed in FIELD# to the spe
cified record number. The first record number in a LB random file is 1 (not 0).
If the length of a given field is shorter than what is specified, LB adds blank
spaces to fill it out. If the length is larger, LB truncates the string. This is
automatic 'left justification' and is a real advantage over Basic, where you mu
st use LSET or RSET to justify the data before using PUT.
GET#filehandle,recordnumber
The GET# statement reads an entire record and fills the variables listed in FIEL
D#. The same list of variables should be in GET# that is in the PUT# which was u
sed to create the record.
GETTRIM#filehandle,recordnumber
GET# above reads each field 'as is' including blank spaces at the end of each fi
eld. GETTRIM removes leading and trailing blank spaces around each variable list
ed in FIELD#. This is something unique to LB and is very handy.
Basically that is the gist of random access files using LB's commands. Here is a
short example that creates two records.
OPEN "TEMP.DAT" FOR RANDOM AS #1 LEN=15 'open file
FIELD#1,10 AS A$,5 AS B$ 'set up fields
A$="123" : B$="456" 'data for first record
PUT#1,1 'write the record
A$="789" : B$="ABC" 'data for second record
PUT#1,2 'write the record
CLOSE #1 'close the file
OPEN "TEMP.DAT" FOR RANDOM AS #2 LEN=15 'open file
FIELD#2,10 AS A$,10 AS B$ 'set up fields
GET#2,1 'read first record
PRINT A$ 'show fields
PRINT B$
GET#2,2 'read second record
PRINT A$ 'show fields

PRINT B$
CLOSE #2 'close file
LIBERTY BASIC LIMITATIONS
LB's file commands are very useful and easy to use but do have a few limitations
.
With a random access file, there is no easy way to read or write to part of a re
cord. GET# and PUT# only deal with whole records. There is no function to positi
on the 'file pointer' to a specific spot in a file or record and then read or wr
ite selected data.
Network access of files is not supported via these commands. Files are opened fo
r single-user access only. On a network, a computer trying to open a file that h
as been opened by another computer generates a sharing violation error.
LB can only read text data. There is no ability to read numeric data created by
a different program and stored in IEEE or another format.
Using the API file functions can overcome some of these limitations. Deanslib.Dl
l functions can be used to overcome others.
DISK FILE HANDLING IN LIBERTY BASIC
By Dean Hodgson copyright (c) 1999 dhodgso-@nexus.edu.au
Part 3 - Windows API disk file commands
Windows has its own set of API disk file handling functions. They work very diff
erently to LB's own field commands and they allow you to do some things that LB
itself does not permit such as sharing networked files and reading or writing da
ta to specific parts of a file.
First, you need to open the API Dynamic Link Libraries (DLL):
OPEN "kernel" for DLL AS #kernel
OPEN "user" for DLL AS #user
Remember to close these at the end of your program with
CLOSE #kernel
CLOSE #user
GetDriveType
This API function can tell you the type of drive that is available.
CheckDrive = 1
CALLDLL #kernel, "GetDriveType", _
CheckDrive AS short,_
result AS word
CheckDrive is a number indicating the drive where 0 is for drive A, 1 is for dri
ve B, 2 for C, etc. The result is a number that indicates the type of drive:
2 floppy disk
3 hard disk
4 network drive
You don't have to insert a disk for this function to work. A Cd-Rom drive often
returns a value of 4.
SetErrorMode
You call this API function before doing any of the others below. And you must ca
ll it again afterwards. This functions turns on/off Windows own file error messa
ges.

ErrorMode = 1
CALLDLL #kernel, "SetErrorMode", _
ErrorMode AS word,_
result AS word
Setting ErrorMode to 1 turns off Windows error messages. What then happens is th
e API calls below all return an error value. Setting ErrorMode = 0 turns Windows
error messages back on. The result can generally be ignored.
As an example, after doing the above, you can try to create a file on drive A. D
o not put a floppy disk in when doing this and you'll see the error. The program
is:
OPEN "kernel" for DLL AS #kernel 'open the API libraries
ErrorMode = 1 'turn off Windows error trapping
CALLDLL #kernel, "SetErrorMode",_
ErrorMode AS word,_
result AS word
File$ = "A:\TEST" 'going to try to create a file
Attrib = 0
CALLDLL #kernel, "_lcreat",_
File$ AS ptr,_
Attrib AS short,_
result AS short
IF result = -1 THEN
NOTICE "An error happened accessing DRIVE A:"
ELSE
KILL File$
END IF
ErrorMode = 0 'turn error trapping back on
CALLDLL #kernel, "SetErrorMode",_
ErrorMode AS word,_
Result AS word
CLOSE #kernel
FILE EXISTS TEST
The API call _lopen can be used to see if a file exists. This function is descri
bed below.
File$ = "TEST" 'filename we are checking for
Type = 16384 'use this value to test for a file
CALLDLL #kernel, "_lopen",_ 'try to open the file
TestFile$ AS ptr,_
Type AS short,_
result AS short
IF result<>-1 THEN NOTICE "File Exists" ELSE NOTICE "File Doesn't Exist"
The result should be 0 if the file exists.
CREATING A FILE _lcreate
Unlike LB's commands, the API makes a difference between creating files and open
ing already existing files. This means you must check for the existence of a fil
e before opening it, if you do not know it is present.

Attrib=0
CALLDLL #kernel, "_lcreate",_
File$ AS ptr,_
Attrib AS short, _
result AS short
Attrib is a value indicating the type of file:
0 normal read/write file
1 read only file
2 hidden file
3 system file
The result is a number assigned by Windows to the open file. If this value is -1
, an error has happened.
Note: If you attempt to create an existing file, all the contents of that file a
re lost!!
OPENING A FILE _lopen
The API does not differentiate between Sequential, Random and Binary files. All
files are essentially Binary when opened but they have certain read/write proper
ties. _lopen is used to open a file that has been created. If you try to open a
file that does not exist, the result will be an error.
FileType=2
CALLDLL #kernel, "_lopen",_
File$ AS ptr,_
Type AS short,_
FileHandle AS short
NOTICE "File handle for "+TestFile$+" is "+STR$(FileHandle)
The result of _lopen is a number called the "File handle". Windows assigns this
value to an open file, unlike LB's commands where you assign the handle. You the
n use the FileHandle value in all subsequent operations on the open file. The va
lue is different each time you open the If there was an error opening, FileHandl
e is -1.
The FileType is important and offers functionality not found in LB's OPEN statem
ent. Here are some of the values you can use when opening files.
0 is read only users can only perform read operations on the file
1 is write only users can only write to the file
2 is read/write users can either read or write to the file
64 is shared read only allows network shared access for reading only
65 is shared write only allows network shared access for writing only
66 is shared read/write allows network shared access for reading and writing
Under normal circumstances, you should set the FileType to 2 for use on stand-al
one computers or 66 for use in a network situation where more than one user can
access the file at the same time. If you don't know the situation in which your
software will be used, 66 is a reasonable default.
If a file has been opened using a 0 and another computer on a network tries to o
pen the same file, they will get an error. If the file is opened using 64 and an
other user tries to open with a 64, no error will occur and both machines will b
e able to read from the file at the same time. However, if another user tries to
open the same file with a different value, they will get an error.
WRITING TO A FILE _lwrite
The function _lwrite can be used to write strings up to 64k long to a file.

S$="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"
LengthOfString=LEN(S$)
CALLDLL #kernel, "_lwrite",_
FileHandle AS short,_
S$ AS ptr,_
LengthOfString AS short,_
result AS short
The exact string will be written to the file as is. No end-of-line markers (CHR$
(13) CHR$(10)) are written. If you want these, you need to add them to the strin
g yourself before writing and include them in the length.
FileHandle is the value returned by _lopen.
LengthOfString is the number of bytes to write. If this value is less than the a
ctual length of the string, then only that number of characters are written. If
the value is greater then an error occurs.
SEEK _llseek
The API contains a function for positioning the 'file pointer' to any spot withi
n a file. This pointer specifies where you will start reading or writing data. N
ote: the pointer is automatically moved to the end of any data that is read or w
rite by _lread or _lwrite. _llseek is used to move the pointer to another positi
on within the file.
Position=5
Option=0
CALLDLL #kernel, "_llseek",_
FileHandle AS short,_
Position AS long,_
Option AS short,_
result AS long
Position is the number of bytes to move the pointer to the beginning of a record
by multiplying the record length by the record number -- 425 * 6 puts the point
er at the start of record 6. Note: that record numbers start with 0 and not 1!!
READING FROM A FILE _lread
This is similar to _lwrite. However, you need to set up a buffer string in order
to receive the data. In the example below, we are going to read 10 bytes from t
he file. Therefore the buffer needs to be at least 10 bytes long. We also need t
o add a CHR$(0) at the end to tell LB to deal with this string properly within t
he DLL call.
Buffer$ = SPACE$(10) + CHR$(0)
Readbytes = 10
CALLDLL #kernel, "_lread",_
FileHandle AS short,_
Buffer$ AS ptr,_
Readbytes AS short,_
result AS short
The value in Readbytes should not be greater than the length of Buffer$! If an e
rror happens, the result will be -1, otherwise it will be the number of bytes re
ad.
CLOSING THE FILE _lclose
CALLDLL #kernel, "_lclose",_
FileHandle AS short,_
result AS short

The result will be 0 if the file was closed properly.


CLEANING UP
Remember to turn error message reporting back on.
wMode = 0
CALLDLL #kernel, "SetErrorMode", _
wMode AS word,_
result AS word
And, of course, to close the API DLL files
CLOSE #kernel
CLOSE #user
An example:
Here is a random access file example using just the API disk calls. It does the
same thing as the example given for LB in the previous part. In order to make th
e functions "general purpose" and reusable, I've put them into subroutines. Also
, those readers clever enough may be able to see how to use structs to hold reco
rd data instead of the method I've employed.
OPEN "kernel" for DLL AS #kernel
OPEN "user" for DLL AS #user
GOSUB [ErrorOff] 'turn off Windows errors
File$="TEMP.DAT" 'The filename
GOSUB [CreateFile] 'Create file (it'll be open)
GOSUB [CloseFile] 'Close it
GOSUB [OpenFile] 'Open the file
A$="123" : NBW=10 : lwrite$=A$ : GOSUB [Write] 'first field,
B$="456" : NBW=5 : lwrite$=B$ : GOSUB [Write] 'second field,
A$="789" : NBW=10 : lwrite$=A$ : GOSUB [Write] 'first field,
B$="ABC" : NBW=5 : lwrite$=B$ : GOSUB [Write] 'second field,
NBW=15 : lwrite$="" : GOSUB [Write] 'blank record at end
GOSUB [CloseFile]
recsize=15
File$="TEMP.DAT" : GOSUB [OpenFile] 'open file
print "Record one"
seek=0 : GOSUB [Seek] 'put pointer at record 0
NBR=10 : GOSUB [Read] : A$=TRIM$(lread$) 'read first field
NBR=5 : GOSUB [Read] : B$=TRIM$(lread$) 'read second field
PRINT A$
PRINT B$
PRINT "Record two"
seek=1 * recsize : GOSUB [Seek] 'pointer to 2nd record (1)
NBR=10 : GOSUB [Read] : A$=TRIM$(lread$) 'read first field
NBR=5 : GOSUB [Read] : B$=TRIM$(lread$) 'read second field
PRINT A$ 'show fields
PRINT B$
GOSUB [CloseFile]
GOSUB [ErrorOn] 'clean up
CLOSE #kernel
CLOSE #user
END
'Opens a file in shared read/write mode

first record
first record
second record
second record

'Pass File$ as the filename


'Returns FileHandle. 0 if an error.
[OpenFile]
FileNumbere=1
CALLDLL #dl,"DOPENR",_
FileNumber AS short,_
File$ AS ptr,_
recsize AS short,_
result AS short
RETURN
'Read string from file
'NBR is number of bytes to read
'Result is returned in string lread$, which does not have spaces
'truncated.
'Result is -1 if an error
[Read]
lread$=""
temp$=SPACE$(NBR)+CHR$(0) 'temporary string to receive data
CALLDLL #kernel, "_lread", FileHandle AS short, temp$ AS ptr, NBR AS
short, result AS short
IF result>=1 THEN lread$=LEFT$(temp$,result) 'strip 0 at end of string
RETURN
'Writes the string lwrite$
'NBW is the number of bytes to write
[Write]
temp$=lwrite$ 'make it a temporary string
temp=NBW-LEN(temp$) 'test to see if string isn't
right length
IF temp>0 THEN temp$=temp$+SPACE$(temp) 'too short, add spaces
IF temp<0 THEN temp$=LEFT$(temp$,NBW) 'too long, truncate
CALLDLL #kernel,"_lwrite",FileHandle AS short,temp$ AS ptr,NBW AS
short,result AS short
RETURN
'seek file pointer, the number of bytes from the start of the file
[Seek]
temp=0
CALLDLL #kernel, "_llseek", FileHandle AS short, seek AS long, temp AS
short, result AS long
RETURN
[CloseFile]
CALLDLL #kernel, "_lclose", FileHandle AS short, result AS short
RETURN
API DISK FILE LIMITATIONS
You can only read and write data via strings. You must convert numeric data usin
g VAL and STR$ functions.
There are no special commands and functions for dealing with sequential and rand
om access files. A file is essentially a block of bytes.
The structure of a random access file is up to you. To read an entire record, yo
u would read the whole record into a string then break it up into your fields us
ing MID$.
DISK FILE HANDLING IN LIBERTY BASIC
By Dean Hodgson copyright (c) 1999 dhodgso-@nexus.edu.au
Part 4 - Deanslib disk file commands

Deanslib is a freeware DLL file containing over 100 functions that can be called
from Liberty Basic. A range of disk file functions is included. The random acce
ss numeric functions read and write integer numeric data types in IEEE format.
Also, the functions are designed for use in a network situation where data needs
to be shared.
Deanslib supports Sequential and Random Access file types.
To initialize the DLL use the command
OPEN "DEANSLIB.DLL" FOR DLL AS #dl
SEQUENTIAL FILES
Opening a file for reading DOPENI
This function opens a sequential file for reading data. The sytax is:
CALLDLL#dl,"DOPENI",_
filenumber AS short,_
filename$ AS ptr,_
result AS short
The filenumber is a unique number that you provide, from 1 to 99. You use this s
ame number for all other calls to the functions below.
The filename is the name of your file. It can include the path. The result indic
ates if there was an error when the file was opened. It is 0 if there was no err
or or not 0 if there was. The file is opened for shared multi-user read-only acc
ess so can be used in network situations.
Opening a file for writing DOPENO
DOPENO is used when you want to write to a sequential file.
CALLDLL#dl,"DOPENO",_
filenumber AS short,_
filename$ as ptr,_
result AS short
The parameters here are the same as for DOPENI.
DPOPENA opens a file for appending extra data at the end:
CALLDLL#dl,"DOPENA",_
filenumber AS short,_
filename$ AS ptr,_
result AS short
Closing a File
CALLDLL#dl,"DCLOSE",_
filenumber AS short,_
result As short
Reading sequential data
There are two functions that can be used to read sequential data. Both deal with
strings only.
buffer$=SPACE$(length)+CHR$(0)
CALLDLL#dl,"DREADS",_
filenumber AS short,_

buffer$ AS ptr,_
length AS short,_
result AS short
DREADS reads data into a string and therefore works similarly to LB s INPUT$ funct
ion. The variable buffer$ is a return string and must contain as many blank spac
es as the number of bytes to be read (length) allocated before calling the funct
ion, and it must end in a CHR$(0). This is the way LB passes data from DLL funct
ions to strings.
The length is the number of bytes to be read.
The result is a 0 if there was no error or not 0 if there was.
buffer$=SPACE$(length)+CHR$(0)
CALLDLL#dl,"DINPUT",_
filenumber AS short,_
buffer$ as prt,_
result AS short
This function works like LB's LINE INPUT#. It reads a string until a CHR$(13) en
d-of-line marker is encountered.
If you are inputting numeric data, read it using these functions as a string the
n change to a number via LB s VAL function. For example:
length=10
buffer$=SPACE$(length)+CHR$(0)
CALLDLL#dl,"DINPUT",filenumber AS short, buffer$ AS ptr, result AS short
number=VAL(buffer$)
Writing sequential data
Only two commands are available. The first writes a single string and adds and e
nd-of-line marker automatically. The result is 0 for no error.
CALLDLL#dl,"DPRINT",_
filenumber AS short,_
string$ AS ptr,_
result AS short
The second is similar to the _lwrite API function and writes the specified numbe
r of bytes from a string.
CALLDLL#dl,"DWRITES",_
filenumber AS short,_
string$ AS ptr,_
length AS short,_
result AS short
RANDOM ACCESS FILES
Deanslib contains a range of functions for dealing with random access files. To
open a random file, use the function
CALLDLL#dl,"DOPENR",_
filenumber AS short,_
filename$ AS ptr,_
recordlength AS short,_
result AS short
The variable recordlength is the number of bytes in each record.
The result is 0 if the file is successfully opened or created.
This function opens files in shared read/write mode which allows multiple access

of the same file on networks.


Use DCLOSE to close the file.
Positioning the File Pointer
This function is similar to the API function _llseek.
CALLDLL#dl,"DSEEK",_
filenumber AS short,_
position AS long,_
result AS short
Position is the number of bytes from the start of the file. If Position is a neg
ative number, it represents the number of bytes from the end of the file. A resu
lt other than 0 indicates an error, such as trying to position past the end of t
he file. To position to a particular record use recordlength * recordnumber to g
et the number of bytes.
Reading Random Access Data
Functions are available for reading and writing numbers in IEEE format and for s
trings.
This function reads a 2 byte signed short integer into result. Filenumber is the
number you assigned in DOPENI.
CALLDLL#dl,"DREADI", filenumber AS short, result AS short
Reads a 4 byte signed long integer value.
CALLDLL#dl,"DREADL", filenumber AS short, result AS long
DREADS reads string data
buffer$=SPACE$(length)+CHR$(0)
CALLDLL#dl,"DREADS",_
filenumber AS short,_
buffer$ AS ptr,_
length AS short,_
result AS short
buffer$ is the string that receives the data.
length is the number of bytes. This cannot exceed 32767 bytes.
result is 0 for a successful read or not zero for an error.

Writing Random Access Data


2 byte short integers are written using
CALLDLL#dl,"DWRITEI",filenumber AS short, value AS short, result AS short
4 byte long integers are written with
CALLDLL#dl,"DWRITEL",filenumber AS short, value AS long, result AS short
Strings are written with DWRITES described previously.

Extra Things

DSHARE
This indicates whether or not SHARE is active. It is normally available if you a
re using Window 3.11, 95, 98 or NT but may not be present under 3.1.
The open functions require SHARE to be present.
CALLDLL#dl,"DSHARE", result AS short
A 0 is returned if not present or -1 if present.
DFLUSH
Data can remain within the computer's internal buffers. This function forces dat
a to be written, which can be
useful before closing a file.
CALLDLL#dl,"DFLUSH", filenumber AS short, result AS void
FILEHANDLE
You can obtain the Windows filehandle value for the open file using this functio
n. Once you have the handle, you can then use the API disk functions _llseek, _l
read and _lwrite as well as the Deanslib functions.
CALLDLL#dl,"FILEHANDLE",filenumber AS short, result AS short
There are also functions dealing with record locking, which is part of networkin
g. This is covered in the final section.

EXAMPLE
Here is the same random access program as written for the API calls but using De
anslib functions instead.
OPEN "DEANSLIB.DLL" for DLL AS #dl
File$="TEMP.DAT" 'The filename
recsize=15 'Set up our record size
GOSUB [OpenFile] 'Open the file
A$="123" : NBW=10 : lwrite$=A$ : GOSUB [Write] 'first field,
B$="456" : NBW=5 : lwrite$=B$ : GOSUB [Write] 'second field,
A$="789" : NBW=10 : lwrite$=A$ : GOSUB [Write] 'first field,
B$="ABC" : NBW=5 : lwrite$=B$ : GOSUB [Write] 'second field,
NBW=15 : lwrite$="" : GOSUB [Write] 'blank record at end
GOSUB [CloseFile] 'close file
GOSUB [OpenFile] 'open file
print "Record one"
seek=0 : GOSUB [Seek] 'put pointer at record 0
NBR=10 : GOSUB [Read] : A$=TRIM$(lread$) 'read first field
NBR=5 : GOSUB [Read] : B$=TRIM$(lread$) 'read second field
PRINT A$
PRINT B$
PRINT "Record two"
seek=1 * recsize : GOSUB [Seek] 'pointer to 2nd record (1)
NBR=10 : GOSUB [Read] : A$=TRIM$(lread$) 'read first field

first record
first record
second record
second record

NBR=5
PRINT
PRINT
GOSUB

: GOSUB [Read] : B$=TRIM$(lread$) 'read second field


A$ 'show fields
B$
[CloseFile]

CLOSE #dl
END
'Opens a file in shared read/write mode
'Pass File$ as the filename, recsize as the record length and FileNumber
[OpenFile]
CALLDLL #dl,"DOPENR",_
FileNumber AS short,_
File$ AS ptr,_
recsize AS short,_
result AS short
RETURN
'Read string from file
'NBR is number of bytes to read
'Result is returned in string lread$, which does not have spaces truncated.
[Read]
lread$=""
temp$=SPACE$(NBR)+CHR$(0) 'temporary string to receive data
CALLDLL #dl, "DREADS",_
FileNumber AS short,_
temp$ AS ptr,_
NBR AS short,_
result AS short
IF result>=1 THEN lread$=LEFT$(temp$,result) 'strip 0 at end of string
RETURN
'Writes the string lwrite$
'NBW is the number of bytes to write
[Write]
temp$=lwrite$ 'make it a temporary string
temp=NBW-LEN(temp$) 'test to see if string isn't right length
IF temp>0 THEN temp$=temp$+SPACE$(temp) 'too short, add spaces
IF temp<0 THEN temp$=LEFT$(temp$,NBW) 'too long, truncate
CALLDLL #dl,"DWRITES",_
FileNumber AS short,_
temp$ AS ptr,_
NBW AS short,_
result AS short
RETURN
'seek file pointer, the number of bytes from the start of the file
[Seek]
CALLDLL #dl, "DSEEK", FileNumber AS short, seek AS long, result AS long
RETURN
[CloseFile]
CALLDLL #dl, "DCLOSE", FileNumber AS short, result AS short
RETURN
Part 5 - Sharing files on networks
Networks allow many users to access programs and files stored on a central compu
ter. There are various networking configurations but to simplify this discussion
we'll assume one computer is a "server" where common programs and files are sto
red and the other computers are "workstations". Visualize the situation where th

e program you are using is stored on each workstation's hard drive but common da
ta files are stored on the Server and all the workstations can access those file
s.
It is possible that two or more workstations ("users") may try to access the sam
e file at the same time. User A might open the file and read or write to it, and
while that is happening User B might also try to open the file and do something
. Dangers lurk here!
What happens if User A does not want anyone else to change the file while they h
ave it open?
What happens when User B tries to write to part or all of the file while User A
is also trying to write to it?
Situation 2 is dangerous and if unprotected can lead to severe data corruption.
Networks do not automatically protect against this possibility. Your program mus
t be able to protect the data you're working with and it must know if that data
is protected or "locked".
There are two methods of protection: file locking and record locking.
File locking involves protecting the entire file so only one user can work on it
at a time. While the file is locked, no other user can use the file. File locki
ng is established when the file is opened. Files can be opened for single-user r
estricted access, for multi-user read (many users can read the file at the same
time) but single-user write, multi-user write and single-user read (this isn't s
ensible actually) and multi-user read/write.
If you are using large random access files, it is possible to permit multi user
reading and writing except at certain records that are being used at the moment.
These records can be locked as well.
Programs such as Microfsoft Word lock a whole document file when you open it. If
anyone else on the network opens that same file while locked, they receive an e
rror and the file is reported as being in use. Dos would report a "sharing viola
tion" error. Liberty Basic opens files in unshared modes so they are essentially
locked as well and produce sharing error messages. There is no easy method in L
B to detect if a file is locked. However, this can be done using the API calls a
nd Deanslib.
File Locking using API Functions
The attribute you assign to a file when it is opened determines whether or not i
t can be shared.
0 is read only single-user read only _OF_READ
1 is write only single-user write only _OF_WRITE
2 is read/write single-user read or write _OF_READWRITE
64 is shared read only multi-user read only
65 is shared write only multi-user write only
66 is shared read/write multi-user read/write
CALLDLL #kernel, "_lopen", File$ AS ptr, Type AS short, Handle AS short
The last three use _OF_SHARE_DENY_NONE ORd with one of the first three.
If a file is opened by user A using an attribute of 2 and user B tries to open t
hat file, _lopen returns to user B an error and the file is not opened. Therefor
e, it is necessary to trap for errors when opening files. The FileHandle returne
d will have a -1 error. What should then happen is that User B's computer should
either produce a message indicating that the file is in use or pause and try op
ening the file again until successful (User A has finished) or a specified numbe

r of attempts have been made and all have failed. The latter is essential to avo
id "deadly embrace" situations where user A's computer has crashed leaving the f
ile open.
For random access files, it is usually safe to allow multi-user read access (att
ribute value 64) but it is necessary to restrict write access to one user (value
s 1 or 2). Any number of users can open and read the file, but if someone wants
to write to it, they'll get an error when trying to open until everyone reading
has closed the file. If the file is open for writing by one user, everyone else
will get an error until that file is closed.
This approach is reasonable where files are not left open for long periods of ti
me. Its major drawback is where a computer has crashed, leaving a file open in t
he process.
Here is a simple example problem that will lock a file. To see it working, you w
ill need to test it on a network by running the program on two computers at the
same time, changing File$ to include the path to the shared file. When run on th
e first computer, the message "File opened successfully" should appear. Don't pr
ess enter (this keeps the program running and the file open). Run the program on
the second computer. The message "File open error" should appear on the second
computer.
OPEN "KERNEL" FOR DLL AS #kernel
temp=1
CALLDLL#kernel,"SetErrorMode",temp AS short,result AS void
File$="TEMP.DAT"
Filetype=_OF_READWRITE
CALLDLL#kernel,"_lopen",File$ AS ptr,Filetype AS word, FileHandle AS word
IF FileHandle<0 OR FileHandle>32767 THEN
PRINT "File open error"
ELSE PRINT "File opened successfully"
END IF
PRINT "Press enter to close file"
INPUT A$
CALLDLL#kernel,"_lclose",FileHandle AS word,result AS void
temp=0
CALLDLL#kernel,"SetErrorMode",temp AS short,result AS void
CLOSE #kernel
PRINT "Close Window"
END
To make the file sharable change the variable Filetype to Filetype=_OF_READWRITE
OR _OF_SHARE_DENY_NONE. The second part indicates the file should be shared and
not deny access to anyone. Other options include_OF_READ and _OF_WRITE and thes
e are always ORd with the sharing constant.
RECORD LOCKING
Record locking is performed in random access files. Individual records are locke
d rather than the whole file. This makes most of the file accessable to users ex
cept for any records that are being modified. However, trapping and dealing with
record locks require more work than file locking.
There are no Win 3.11 API calls to lock records. The only way to lock records wh
en using the API disk functions is to use the Deanslib LOCK and UNLOCK functions
, described below. These functions contain bits of machine code that perform loc
king via behind-the-scenes Dos calls.
DLOCK

This function locks a range of bytes in a file opened with DOPENR.


CALLDLL#dl,"DLOCK",_
filenumber AS short,_
position AS long,_
count AS long,_
result AS short
Position is the number of bytes from the start of the file where you want to sta
rt locking. Byte 0 is the first byte. For records it is recnumber * reclength.
Count is the number of bytes you want to lock. For most files, this is the lengt
h of the record, although it could span any number of records.
Result = 0 if the lock was successful, indicating that no other system had locke
d the selected range. Any other value is an error, probably indicating that some
or all of the bytes are already locked.
DUNLOCK
This unlocks locked bytes. You must call this to release the lock section.
CALLDLL#dl,"DUNLOCK",_
filenumber AS short,_
position AS long,_
count AS long,_
result AS short
The parameters are identical to DLOCK. Make sure that position and count are the
same as in the locking call.
DLOCKWAIT
This works like DLOCK but if an error occurs when the lock is attempted -- usual
ly indicating that the bytes are already locked -- the lock attempt is repeated
every 3 seconds until a maximum of 25 tries has been made. DLOCK does not wait b
ut returns an error if a region is already locked.
Use DUNLOCK to unlock.
CALLDLL#dl,"DLOCKWAIT",_
filenumber AS short,_
position AS long,_
count AS long,_
result AS short
LOCK
This function locks a region of bytes but you supply the file handle value rathe
r than the filenumber. LOCK is intended to be used when you are using API functi
ons.
CALLDLL#dl,"LOCK",_
FileHandle AS short,_
position AS long,_
count AS long,_
result AS short
UNLOCK
This unlocks the bytes locked by LOCK. Again, the file handle is used.
CALLDLL#dl,"UNLOCK",_

FileHandle AS short,_
position AS long,_
count AS long,_
result AS short
Below is a simple example of record locking to show that it works. The file TEM
P.DAT is opened using DOPENR which does not restrict access. The function DLOCK
is used to lock the first record of 10 bytes. If the result is 0, the record is
unlocked else it is assumed to be locked. Run this program on the first computer
but don't press enter, then run it on the second computer which should indicate
that the record is locked.
OPEN "DEANSLIB.DLL" FOR DLL AS #dl
File$="TEMP.DAT"
Filenumber=1
recsize=10
CALLDLL#dl,"DOPENR",Filenumber AS short, File$ AS ptr, recsize AS word,
result AS word
position=0
count=10
CALLDLL#dl,"DLOCK",Filenumber AS short,position AS long,count AS
long,result AS short
IF result=0 THEN
PRINT "Record locked"
PRINT "Press enter to unlock and close file"
INPUT A$
CALLDLL#dl,"DUNLOCK",Filenumber AS short,position AS long,count AS
long,result AS short
PRINT "Record unlocked"
ELSE
PRINT "Record locked"
END IF
CALLDLL#dl,"DCLOSE",Filenumber AS word,result AS short
CLOSE #dl
PRINT "Close Window"
END
For the technically minded, here are the machine code calls needed to lock and u
nlock records. The starting position is a 4-byte 32-bit long integer. The CX reg
ister contains the high word of the starting position, and DX contains the low w
ord. The length is also a long int with the high word in SI and the low word in
DI. The AH register contains $5Ch and AL contains a 0 to indicate a lock or a 1
to unlock. BX contains the Dos file handle. The call is Interrupt $21h service $
5Ch. If successful the CX register contains 0, unsuccessful CX contains 1 and th
e AX register is the error code. Please note that Dos does not automatically unl
ock a file region when a program terminates or a file is closed!
SEMAPHORE LOCKING
Semaphore locking is a trick commonly used in industrial software. A semaphore i
s a flag that is raised when an event occurs. The actual data files and records
being worked on are not locked. Instead a special file is locked and set up to c
ontain information as to who is locking what. This is the flag, a notice to ever
yone that something important is happening, so hands off! Semaphore locking is a
lso known as a "soft lock" as opposed to a "hard lock" described above and can o
vercome some of the problems of hard locks.
There are two methods. In one, a special file is opened and locked before dealin
ig with any other file. All file operations of the program check the status of t
his file before accessing any other files. If the semaphore file is already lock
ed, the program waits and tries again, repeating until the lock has been cleared
by the locking computer or until a specified time out.

Another variant of soft locking is to reserve a single byte at the beginning of


each data record. This byte indicates whether or not the record is currently loc
ked. Perhaps an "L" indicates this. Therefore, to lock a record, you first retri
eve it and check to see if is already locked.
If not, assign an "L" and write the record back. Any other computer can tell if
that record is locked by examining the first byte. An enhancement to this approa
ch might also store user identificati
Dean Hodgson
BookMark Project -- School Library Automation Software
Department of Education, Training and Employment Adelaide, South Australia
email to: dhodgso-@nexus.edu.au
website: http://www.nexus.edu.au/bookmark/
phone 0011-61-8-8226-1541 fax 0011-61-8-8410-2856
http://www.libertybasicuniversity.com/lbnews/nl50/thirtythree.html

You might also like