Definition: I/O means input and output.
This lesson explains how you can use Visual Basic code to
manage disk file I/O. If you've collected data from the user and stored that data in variables and arrays, you can save the data to the disk for later retrieval. Also, you can access disk files
from within Visual Basic for product inventory codes,
amounts, customer balances, and whatever else your program needs from the long-term data file storage.
As you master Visual Basic and upgrade to other Visual Basic products, you'll add additional controls to your Toolbox window. There are several
database access controls that read and write the data you've put in databases using products such as
Microsoft Access and Paradox. Even though these controls provide more power and ease than you can get by programming alone, you'll still need the
fundamentals of disk access. This unit explains a little on the background of disk access and teaches some of
the most important disk commands and functions that you need to work with data files.
Concept: A file is a collection of related data as well as programs that you buy and write, and documents from your
word processor. Generally, you'll use Visual Basic to access data and text files stored on the
disk.
There are all kinds of files on your computer's disks. Every file is stored under a unique filename to its directory and disk drive. Therefore,
there can't be two or more files with the same filename unless the files reside in different directories or
on different disks.
Definition: A data file holds data on the disk.
This unit is concerned with data files. Data files can take on all kinds of formats. Generally, newcomers to Visual Basic should stick with data files that are textual in nature. Text
files are readable by virtually any kind of program, and virtually
any program can produce text files. Sometimes, text files are called ASCII files because text files consist of strings of ASCII characters.
Before Visual Basic can access a file,
you or the user will have to direct Visual Basic to the exact location on the exact disk where the file is stored. If your user is selecting a file, you'll want to use the file selection frame described in the
previous unit to give the user the ability to
change drives, directories, and filenames easily. When your program accesses a file that the user doesn't know about, your program will have to supply the drive, directory, and filename.
Note: The project at the end of this lesson contains an application that combines the file dialog frame that you mastered in the previous unit with the file I/O commands and functions described here to build a complete file access and display program.
Review: This unit teaches you how to access text data files stored on the disk. You'll need to supply Visual Basic with the filename,
directory, and disk drive of any file with which Visual Basic works.
Concept:
The Open statement opens files. Before Visual Basic can access a data file, Visual Basic has to open the file first. Think of the Open statement as doing for Visual Basic what an open file drawer does
for you when you want to retrieve a
file from a filing cabinet. The Open statement locates the file and makes the file available to Visual Basic.
The Open statement performs various tasks such as locating a file, making sure that the file exists if needed, and creating some
directory entries that manage the file while the file is open. A Visual Basic program always has to open a file, using Open,
before the program can read or write data to the file.
Here is the format of Open:
Open FileNameStr [For mode] As [#]FileNumber
The FileNameStr must be a string value or variable that holds a filename. The filename must reside on the default drive or directory unless you specify the full path to
the file. Generally, you won't have easy access to the user's current Windows
default drive and directory, so you'll almost always specify the full drive and pathname inside the FileNameStr portion of the Open statement.
The mode
must be a named value from Table 18.1. There are additional mode values, but this book won't cover the more advanced or the esoteric mode values. The mode tells Visual Basic exactly what your program expects to do
with the file once
Visual Basic opens the file.
Mode | Description |
Append | Tells Visual Basic that your program needs to write to the end of the file if the file already exists. If the file doesn't exist, Visual Basic creates the file so that your program can write data to the file. |
Input | Tells Visual Basic that your program needs to read from the file. If the file doesn't exist, Visual Basic issues an error message. As long as you use a file selection frame properly, Visual Basic will never issue an error, because the file selection frame forces the user to select a file or cancel the selection operation. |
Output | Tells Visual Basic that your program needs to write to the file. If the file doesn't exist, Visual Basic creates the file. If the file does exist, Visual Basic first erases the existing file and creates a new one under the same name, thereby replacing the original one. |
The pound sign, #, is optional, although
most Visual Basic programmers do specify the pound sign out of habit (some previous versions of the BASIC language required the pound sign). The FileNumber represents a number from 1 to 255 and
associates the open file with that number. After you
open a file successfully (assuming that there are no errors such as a disk drive door being left open), the rest of the program uses file I/O commands and functions to access the file. The file number
stays with the file until you issue a Close command
(see the next section) that releases the FileNumber and makes the number available to other files.
Note: As with all DOS and Windows file descriptions, you can specify the drive, directory, and filename using uppercase or lowercase characters.
You can open more than one file simultaneously within a single program. Each command that accesses one of the files
directs its activity towards a specific file using that file's FileNumber.
The following Open statement creates and opens a data file on the disk drive and associates the file to the file number 1:
Open "d:\data\myfile.dat" For Output As #1
If you knew that the file already existed and you needed to add to the file, you could use the Append mode to add to the file after this Open statement:
Open "d:\data\myfile.dat" For Append As #1
One Visual Basic program can have more than one file open at the same time. There is an advanced FILES option in your computer's CONFIG.SYS file that
determines the maximum number of files that can be open at one time. If the #1 FileNumber was in
use by another file that you opened earlier in the application, you could assign the open file to a different number like this:
Open "d:\data\myfile.dat" For Append As #5
Any currently unused FileNumber works; you can't associate more than one file at a time to the same FileNumber value.
The following Open
would open the same file for input in a different program:
Open "d:\data\myfile.dat" For Input As #2
Visual Basic supplies a helpful built-in function named FreeFile() that accepts no
arguments. FreeFile() returns the next available file number value. For example, if you've used #1 and #2 for open files, the next value returned from FreeFile() will be
3. FreeFile() is most helpful when you write general-purpose subroutine and function
procedures that need to open files, and the procedures may be called from more than one place in an application. At each calling location, there is a different number of
files open at the time. The procedure can store the value of the next available file
number like this:
fNum = FreeFile()
and use the variable fNum in subsequent Open, I/O, and Close statements. No matter how many files are open, the procedure will always use the next file number in
line to open its file.
Review: The Open command associates files using file numbers with which the rest of the program will access the file. The three mode values determine how Visual Basic uses
the file. If you want to write
to a file, you can't use the Input mode, and if you want to read from a file, you can't use Output or Append.
Concept: The Close statement performs the opposite job from Open. Close closes the file by writing any final data to the file, releasing the file to other
applications, and giving the file's number back to your
application in case you want to use that number in a subsequent Open statement.
Eventually, every program that opens files should close those files. The Close statement closes files. These
are the two formats of Close:
Close [[#]FileNumber] [, ..., [#]FileNumber]
and
Close
The first format closes one or more
open files, specifying the files by their open file numbers. The pound sign is optional in front of any of the file numbers. The second form of Close closes all files that are currently open. Close closes any open
file no matter what mode you used to open
the file.
Tip: If you create a file by opening the file with the Output mode, and then close the file, you can reopen the same file in the same program in the Input mode to read the file.
The following statement closes the two open files that were opened and attached to the 1 and 3 file numbers:
Close 1, 3
The following statement closes all files no matter how many are open:
Close ' Closes ALL files
Review: Use the Close statement to close all open
files before your program ends. Closing files provides extra safety, so close any and all open files when you're through accessing those files. If a power
failure occurs during your program's execution, your closed files will be safely stored on the disk,
but any files that are open at the time of the power failure could lose data.
Concept: The Write# command is perhaps the easier command to use for writing data to a file. Write# writes data of any data type to a file. Using corresponding input statements that you'll learn in the next
section,
you'll be able to read data that you sent to a file with the Write# command.
The Write# statement enables you to write data of any format to any disk file opened in the Output or Append mode. Write# writes strings, numbers, constants, variables,
in any and all combinations to a disk file.
Here is the format of Write#:
Write #FileNumber [, ExpressionList]
The FileNumber must be a file number associated to a file opened with
Output. If you don't specify variables or values to write, Write# writes a carriage return and line feed character (an ASCII 13 followed by an ASCII 10) to the file, putting a
blank line in the file. If you specify more than one value in the
ExpressionList, Visual Basic writes that data to the file using the following considerations:
The code in Listing 18.1 writes a famous first century poem to a disk file named ODE.TXT. A command button named cmdWrite triggers the code's execution with
its Click event when the user clicks the command button. The file appears on your C: drive's
root directory.
Listing 18.1. Writing four string values to a file named ODE.TXT.
1: Sub cmdWrite_Click () 2: ' Creates a text file and 3: ' writes a poem to the file 4: Open "c:\ode.txt" For Output As #1 5: 6: ' Write the poem 7: Write #1, "Visual Basic, Visual Basic," 8: Write #1, "oh how I long to see..." 9: Write #1, "A working application that" 10: Write #1, "means so much to me." 11: 12: ' Always close any open file 13: ' when done with the file 14: Close #1 15: End Sub
When Visual
Basic closes the file in line 14, there will be a file in the user's root directory with the following contents:
"Visual Basic, Visual Basic," "oh how I long to see..." "A working application that" "means so much to me."
Typically, quotation marks never appear when you assign or display string values. The quotation marks are usually for the programmer to indicate the start and end of string values used in a
program. The only exception to the appearance of quotation
marks is that Write# always adds the quotation marks around all string data, whether the data is variable or constant, that Write# outputs to a file.
Definition: Append means to add to the end of something.
If you open a file using the Append mode, the Write# statement will add to the end of the
file. The program in Listing 18.2 writes a blank line and a second stanza to the poem stored in the ODE.TXT file.
Warning: Remember that if you write to a file that already exists using the Output mode value, Visual Basic will erase the original contents and replace the file with the subsequent writing.
Listing 18.2. Code that adds to
the end of a data file using the Write# statement.
1: Sub cmdAddIt_Click () 2: ' Adds to a text file already created 3: ' by writing a second verse to the file 4: Open "c:\ode.txt" For Append As #1 5: 6: ' Add to the poem 7: Write #1, ' Writes one blank line 8: Write #1, "Visual Basic, to you I sing" 9: Write #1, "the songs a programmer knows..." 10: Write #1, "Listen carefully when I choose Run," 11: Write #1, "or we'll surely come to blows." 12: 13: Close #1 14: 15: End Sub
I'll give you a chance to get a handkerchief before looking at the newly expanded poem. Here is the result of the Append mode value if you
were to display the contents of the ODE.TXT file after running this event procedure:
"Visual Basic, Visual Basic," "oh how I long to see..." "A working application that" "means so much to me." "Visual Basic, to you I sing" "the songs a programmer knows..." "Listen carefully when I choose Run," "or we'll surely come to blows."
Tip: You may write data to files from variables as well as from controls on the form. Wherever you've got data that needs to be written, Visual Basic's Write# command will write that data to a disk file that you've opened.
Stop and Type: Listing 18.3 contains a subroutine procedure that accepts four arrays of four different data types and writes that array data to a disk file named
VALUES.DAT. Notice how a simple For loop can be
used to write a large amount of data to a data file. The fifth argument sent to the subroutine is assumed to contain the total number of elements defined for the arrays.
Review: The Write# statement provides one of the easiest file output commands inside the Visual Basic language. Write# separates multiple output values with commas and encloses all string data inside quotation
marks.
Listing 18.3. Code that writes arrays using the Write# statement.
1: Sub WriteData (CNames() As String, CBalc() As Currency, CDate() As Variant, CRegion() As Integer) 2: ' Writes array data to a file 3: Dim ctr As Integer ' For loop control 4: ' Assumes that each array has the 5: ' same number of elements defined 6: Dim MaxSub As Integer 7: MaxSub = UBound(CNames) ' The maximum subscript 8: 9: ' Write MaxSub lines to the file 10: ' with four values on each line 11: Open "c:\mktg.dat" For Output As #1 12: For ctr = 1 To MaxSub 13: Write #1, CNames(ctr), CBalc(ctr), CDate(ctr), CRegion(ctr) 14: Next ctr 15: Close #1 16: 17: End Sub
Output: Here are six sample lines from the MKTG.DAT file that the program in Listing 18.3 might write:
"Adams, H", 123.41, #1997-11-18 11:34:21#, 6 "Enyart, B", 602.99, #21:40:01#, 4 "Powers, W", 12.17, #1996-02-09#, 7 "O'Rourke, P", 8.74, #1998-05-24 14:53:10#, 0 "Grady, Q", 154.75, #1997-10-30 17:23:59#, 6 "McConnell, I", 9502.32, #1996-07-12 08:00:03#, 9
Review: Line 1 defines the subroutine procedure and accepts the four passed arrays. Each array is assumed to have the same number of defined subscripts. Although not all arrays passed
to a procedure would
necessarily have the same number of subscripts defined, these happen to do so. Line 6 and 7 defines and initializes a value that holds the maximum number of subscripts, so the subsequent For loop can write all of the array values to
the file names
MKTG.DAT.
The For loop in lines 12 to 14 step through the array elements, writing a line of four array values with each iteration. Visual Basic encloses the string array data with quotation marks and encloses the variant date and
time data with pound signs.
The pound signs around the date and time variant values help Visual Basic when you subsequently read the data values back into variant variables. As you can see, the date may have a missing time or the time may have a
missing date. Write# still writes
as much of the date and time as is available within that variant value.
The Close statement on line 15 closes the file and releases the file number back to the program.
Concept: The Input# statement reads data from files and stores the file data in
your program's variables and controls. Input# is the mirror-image statement to the Write# statement. Use Input# to read any data
that you send to a file with Write#.
The Input# statement reads data into a list of variables or controls. Here is
the format of Input#:
Input #FileNumber [, ExpressionList]
Again, the bottom line to using Input# is that Input# is the mirror image of the Write# statement that produced the file data. When you
write a program that must use data from a data file, locate the program's Write# statement that originally created
the data file, and use that same format for the Input# statement.
Tip: Be sure to open all files that you'll read from using the Input file mode value, or Visual Basic will display an error message.
Listing 18.4 reads and displays in a scrolling list box the
poem stored in the ODE.TXT file that was created in earlier listings.
Listing 18.4. Code that reads a poem using the Input# statement.
1: Sub cmdList_Click () 2: ' Reads a poem from a text file and 3: ' adds the poem, one line at a time, 4: ' to the list box 5: Dim ctr As Integer ' For loop control 6: Dim PoemLine As String ' Holds each poem line 7: Open "c:\ode.txt" For Input As #1 8: 9: ' Read the poem 10: For ctr = 1 To 9 11: Input #1, PoemLine 12: lstPoem.AddItem PoemLine 13: Next ctr 14: 15: ' Always close any open file 16: ' when done with the file 17: Close #1 18: 19: End Sub
Figure 18.1 shows the list box
that would appear as a result of Listing 18.4's execution.
Figure 18.1. The contents of a file shown in a list box.
Definition: A record is a row in a file.
When reading data from a file, you can very easily cause an error by attempting to read more data than the file holds. Listing 18.4 assumed that there were
only nine records in the poem file to read, and that happened to be the case. For data files that
hold data such as customer balances and employee pay values, however, the number of records varies because you'll add and remove records as transactions take
place.
The Eof() function is Visual Basic's built-in end of file function that senses when an input reaches the end of file. Here is the format of the Eof() function:
Eof(FileNumber)
Eof() returns True if the most recent reading of the input file just reached the end of the file and returns False if the input file still has data left to be read. Most data input programs loop until the Eof() condition is true. Perhaps the best way to
use Eof() is with a Do Until-Loop that follows this general format:
Input #1, VariableList ' Read the first record Do Until (Eof(FileNumer) = True) ' Process the record just read Input #1, VariableList ' Get more data Loop
If there are none, one, or four hundred records in the file, this format of Do Until will keep reading, but will stop as soon as the end of file is reached. Many programmers often increment an integer counter variable inside the
loop to count the number
of records read. The counter is useful if you're reading the file's data into arrays.
Note: If you read file data into arrays, be sure to dimension more than enough array elements to hold the maximum number of records expected.
Stop and Type: Listing 18.5 reads the file written earlier using a series of
Write# statements back in Listing 18.3. The body of the code isn't shown. The code is supposed to output the file's contents to a
printed paper report. You won't master the reporting commands until Lesson 11, so comments were used in place of the actual
reporting commands.
Review: As long as you output to files using Write#, you'll easily read that same data back again using Input#. Input# is the mirror-image command to Write#. The combination of Write#
and Input# makes file I/O
simpler than possible in most programming languages.
Listing 18.5. Reading and reporting file data using Input#.
1: Sub ReadData () 2: ' Reads array data from a file and reports the data 3: ' Assume that 200 values were read 4: Static CNames(200) As String, CBalc(200) As Currency 5: Static CDate(200) As Variant, CRegion(200) As Integer 6: Dim NumVals As Integer ' Count of records 7: Dim ctr As Integer ' For loop control 8: 9: NumVals = 1 ' Start the count 10: ' Reads the file records assuming 11: ' four values on each line 12: Open "c:\mktg.dat" For Input As #1 13: Input #1, CNames(NumVals), CBalc(NumVals), CDate(NumVals), CRegion(NumVals) 14: Do Until (Eof(1) = True) 15: NumVals = NumVals + 1 ' Increment counter 16: Input #1, CNames(NumVals), CBalc(NumVals), CDate(NumVals), CRegion(NumVals) 17: Loop 18: 19: ' When loop ends, NumVals holds one too many 20: NumVals = NumVals - 1 21: 22: ' The following loop is for reporting the data 23: For ctr = 1 To NumVals 24: ' Code goes here that outputs the array 25: ' data to the printer 26: ' 27: Next ctr 28: Close #1 29: 30: End Sub
Analysis: Lines 4 and 5 define four arrays that will hold the file's data. The arrays are defined to hold, at most, 200 values. There is no error checking to be sure that the reading doesn't read more than 200
records, but an exercise at the end of the chapter gives you the chance to add this array-checking code.
Line 9 initializes NumVals to hold the count of file records. The count begins at 1 because the first array subscript that will hold incoming
data is 1. (As with all programs in this book, this code ignores the 0 subscript that all arrays contain unless
you specify Option Base 1.)
Line 12 opens the file for input and associates the file to the file number 1. The first input occurs on
line 13. The input attempts to read the first record in the file that contains four values. Line 14's relational test for an end of file condition
will immediately be true if the file is empty. Otherwise, the body of the loop executes. Line 15 increments
the record count once more owing to line 16's input into the next set of array elements. The loop continues until the end of file is reached and
line 14's relational test becomes false.
Line 20 must decrement the NumVals variable by 1 because
the last attempt at reading the file will always produce an end-of-file condition. Therefore, the final Input# was unsuccessful and the record count can't contain that unsuccessful read.
After you read all the data into the arrays, you can work
with the array data just as you normally work with array data. You can calculate statistics based on the data, display the array using list boxes and combo boxes, or print the data to paper.
Concept: The Line Input# command reads data from open data files. Unlike Input#, Line Input# reads each
line of data in the file into a string variable. You don't have to specify separate variable names after a
Line Input# because Line Input# requires a single string value. Line Input# reads data from any file whose lines end with a carriage return and line
feed sequence. (Most files do.)
The Line Input# command is simple to use for reading entire records into a single variable. Whereas Input# reads each records values individually, Line Input# reads an entire recorddata, commas, quotation
marks, and everything elseinto a
string or variant variable or control.
Here is the format of Line Input#:
Line Input #FileNumber, VariableName
No matter how many record values
appear in the file associated with file number 3, the following Line Input# statement reads an image of the record into the string variable named aRecord:
Line Input #3, aRecord
Review: The Line Input# command reads records from data files into string or variant variables. The Input# command reads each record's individual data values into a single variable (or control). The Line Input#
command
reads entire records into string or variant variables. The project at the end of this lesson uses Line Input# to read an entire file using a single string variable.
Write a subroutine procedure that writes four records of your four best
friends' first names, ages, weights, and IQs (remember, these are your friends, so be gentle). Write a second procedure that reads that data into string variables one record at a
time.