Objectives of the Week


Overview of File Processing

Files are needed because we need to store information on a permanent basis. The data written to disk will be available when we want to use it again, and it will be available for other programs to use as well.

There are many type of files and many different file formats. BASIC has the ability to access or create any type of file that we want. For many file formats though, the programming involved may be prohibitive, but the tool set will support the low-level I/O functions. We will concentrate on text files and on accessing them sequentially or randomly.

File processing is made available in VB through a number of functions. These functions are really remnants from when Visual Basic wasn't Visual. In generic terms, in order to process a file you must first OPEN it and when you finish up, you must CLOSE it. To store a piece of information, you must WRITE it to the file, and to retrieve the information from a file you must READ it from the file.

What does it mean to Open a File? In short, it means that you are making that file available to your program. The Open statement requests from the operating system the space that it needs to manage the file. This space is made up of a file management control block and a number of buffers that will stage the information being read or written to the disk. The Open statement will often read the first few blocks of data into the allocated buffers.

Disk I/O is very slow. The functions do a pretty good job of minimizing the PHYSICAL I/O that takes place. The cost in time for reading small blocks of data is very high, so large blocks of data are read into the buffers, and each LOGICAL I/O takes the requested information from the buffer rather than physically reading the disk. The same is true on a write instruction. The data to be written to disk is placed in a buffer and after there have been enough writes to fill the buffer, the large block in then physically written to the disk. You must always be sure to close the file when you are done as this step will "flush" the remaining buffers out to the disk. All of this is invisible to the programmer and is most evident when reading from or writing to a floppy. Often, when processing small text files from a diskette, you see the disk light go on when the file is opened, and if any data is to be written back to disk, again when then the file is closed.

One other point that I think should be emphasized is that it is ONLY at open time that you refer to a file by its PHYSICAL name. On any read, any write, and when the file is closed, the it is referred to SYMBOLICALLY using a defined variable.

Visual Basic provides four modes of processing for text files

Input Sequential File must exist
If it does not, an error is raised
Input
Output Sequential File will be created and if a file with this name exists, it will be destroyed Output
Append Sequential If the file exists, it will be opened for output and positioned at the end
If the file does not exist, it will be created
Output
Random Random Open for both Input and Output processing
If the file does not exist, it will be created
Input and Output.
Fixed size records


FreeFile

FreeFile is a function that is built into Visual Basic and it is called to acquire a unique identifier that can be used to symbolically refer to a file. It is used in conjunction with the Open statement, and then used on every I/O operation performed on the opened file.

Dim intFN as Integer

intFN = FreeFile

I use intFN for File Number, but you can use a name that has meaning to the program you are writing. If you were processing a Student File, you may call it intStudent or maybe fnStudent.

Open

To open a file, you must provide the physical file name, that is the full file specification that consists of the Drive, the Path, the  File Name and the Extension. Gaining access to the full filespec can be done in a number of ways. The full filespec is returned from the common dialog, or you may use the Drive, Directory and File ListBoxes that will be demonstrated in the first example, or you can construct the full file name using App.Path and the name of the file. The open function also needs the processing mode and the file id provided by FreeFile.

intFN = FreeFile
Open strFileName For Input As intFN

This example will open the file name stored in the string strFileName for Read Only

Close

When you are all done processing the file you must issue the close function. Close will flush any buffered information out to disk and release the resources that were allocated at open back to the operation system. Failure to close a file may result in "unpredictable results".

Close intFN


Sequential File Processing

Sequential access will be used in two different ways. First, we can process a text file as a single block of text. There will be no attempt to segment the content into individual records. There will be no delimiter to indicate a logical break in the text.The second way will allow the building and processing of data files consisting of logical records within the text. These records will generally be delimited with a Carriage Return / Line Feed combination. In both cases, the text can be supported using a text editor, such as WordPad or NotePad.

Sequential File Processing Functions

Input Input(intBytes, intFN) Returns all of the characters it reads, including commas, carriage returns, linefeeds, quotation marks, and leading spaces.
LOF LOF(intFN)  Returns the length of the file in Bytes

When processing an entire text file, the Input and LOF functions work well together. Use the LOF function to determine the size of the text file, then pass this value to the Input function to, in effect, read the entire file.

Line Input Line Input intFN, strIOArea The Line Input statement reads from a file one character at a time until it encounters a CR/LF combination. The CR/LF is skipped.
Print Print intFN, strIOArea Writes display formatted data to the file and appends a CR/LF.

Data read with Line Input is usually written to the file with Print.

EOF EOF(intFN) Returns a True when an attempt is made to read beyond the last record in the file

Note that all of these functions use the symbolic reference to the file and not the physical filespec.


File Viewer Application

Download this Example

This example uses the Drive, Directory, and File List Boxes, along with an Input statement that reads the entire file into a TextBox. It also uses the LoadPicture function to display the content of graphic files.

Private Function GetFileText(strFileName As String) As String
    Dim lngLength As Long

    Dim intFN As Integer
    Dim strInput As String

    intFN = FreeFile
    Open strFileName For Input As intFN
    lngLength = LOF(intFN)
    If lngLength < 32767 Then
        strInput = Input(lngLength, intFN)
    Else
        strInput = "Sorry, but the file is too large"
    End If

    Close intFN

    GetFileText = strInput

End Function

The value returned from this function is stored in a TextBox with MultiLine = True and the ScrollBars set as needed.

 

imgPic.Picture = LoadPicture(frmFile.strFileSpec)

This example makes a point to use the frmFile form as a dialog to return the file name, much like the Common Dialog control does.

Here are a couple of functions you might find handy


Error Handling

You have without a doubt experienced the way Visual Basic handles run-time errors. VB will politely display a (somewhat) helpful error message, and then rudely terminate your program. The error handling works a little differently in the design environment as you get the option to Debug the program and VB will position you at the offending statement.

As programs become more advanced, there are certain run-time errors that are simply not avoidable and rather than let VB handle the errors (by terminating the program), you can trap the run time errors and then react to specific errors that you can anticipate with a specific action.

Coding On Error in a VB event handler will trap ANY run time error that occurs. This condition is in effect for the duration of the event handler. When a run time error occurs, control is passed to the label associated with the On Error condition. This label must be within the event handler.

On Error GoTo ErrorRoutine Pass control to ErrorRoutine when a run time error occurs
On Error GoTo 0 Turn off error handling
Resume Next Resume execution of the program at the instruction following the statement that generated the error.
Resume Resume execution of the program at the instruction that generated the error. This essentially means to retry the operation.

The Structure of an Event Handler that Contains Error Handling

It is generally easiest to implement an error handler by building an event handler that is separated into two separate sections. The normal processing path occurs when there is no run time error raised. When the normal processing completes, the Exit Sub instruction will bypass the exception processing. Whenever a run time error occurs, control is immediately passed to the label specified with the On Error statement coded at the beginning of the Event Handler.

On Error

 

The Err Object

The Err Object contains information related to the error condition that was detected. The Error Code and a Text Description of the error are placed in Err.Number and Err.Description. Error Numbers between 1 and 1000 are reserved for Visual Basic. The description contains the same text value that you see when your program terminated when an unhandled run time error is generated. You can check the value of Err.Number when looking to handle specific anticipated errors.

VB even gives you the ability to generate your own run time errors. On the surface, this might sound like an absurd feature, but it allows you some flexibility in the design and the testing of your application. When testing, you may be aware that a certain condition can occur under a circumstance that you cannot readily recreate. You can test your error handling logic by "manually" generating the error. As a design feature, it is a good thing to spilt the event handler into a normal processing segment and an error processing segment. By allowing you to generate your own error, and force a jump to the error handler, you maintain the separate normal and error handling paths.

These features are demonstrated in these two code snippits:

Private Sub cmdDisk_Click()
    Dim rc As Integer

    On Error GoTo DiskError
    Dir "a:"

    lblMessage = "No Problem with Disk Access"

    Exit Sub

DiskError:
    Select Case Err.Number
        Case 52
            rc = MsgBox("Disk Error", _
                                        vbAbortRetryIgnore + vbCritical, _
                                             "Disk Error")
            If rc = vbRetry Then
                lblMessage = "Disk Error: Retry"
                Resume
            ElseIf rc = vbAbort Then
                lblMessage = "Disk Error: Abort"
                On Error GoTo 0
                Resume
            Else
                lblMessage = "Disk Error: Ignore"
                Resume Next
            End If
        Case Else
            lblMessage = "Error: " & Err.Number & _
                                     " Message: " & Err.Description
    End Select

End Sub



' Set the Error Handler
' Access the Disk

' Normal Path

' Exit Normally

' The Error Handler

' Looking for a Code 52
' Give the user an option


' Let them Retry the Operation


' Abort will crash the program




' Just pretend it did not happen

 

Private Sub cmdFile_Click()

    Dim intFN As Integer
    Dim lngFileSize As Long

    On Error GoTo FileError
    CD.ShowOpen

    intFN = FreeFile
    Open CD.FileName For Input As #intFN
    lngFileSize = LOF(intFN)
    If lngFileSize > 32767 Then
        Error 1001
    End If

    lblMessage = "No problem with Text File"

    Exit Sub

FileError:
    Select Case Err.Number
        Case 32755
            lblMessage = "Dialog Cancelled"

        Case 1001
            lblMessage = "Text File is Too Big for a TextBox"

        Case Else
            MsgBox Err.Number & ":" & Err.Description

       End Select

End Sub





' Set the Error Handler
' Show the Common Dialog


' Open the File


' Locally Generated Run Time Error






' Error Handler

' The user pressed cancel on the Dialog


' Handle the forced run time error



' Number and Description for anything else

Error Handling should be implemented anywhere that a run time error could occur. This is sometimes difficult to anticipate so it might not be unreasonable to setup error handling in every event handler. The default action might be to display the VB generated message but it will also prevent the program from crashing.

Download - The Mechanics of Error Handling

Download - File Access Error Handling


Sequential File Processing Example

This example will process a text file sequentially. Each record read is added to a Combo Box. There is some basic error handling involved although the effect is to ignore any error associated with the file processing. In this case, if the High School Text file does not exist, the error that would be generated on Open is ignored.

Download the Example

The Combo Box is initialized using values from the HighSchool Text file. The initial processing takes place in Form Load. The file is opened, and as each record is read, the contents are added to the Combo Box. The file is Closed after all the records have been processed.

Private Sub Form_Load()
    Dim FN As Integer
    Dim IOArea As String

    On Error GoTo NoFile

    FN = FreeFile
    Open strFileSpec(FileName) For Input As FN
    Do While Not EOF(FN)
        Line Input #FN, IOArea
        cboSchool.AddItem IOArea
    Loop
    Close FN
    Exit Sub

NoFile:

End Sub

When the program terminates, the contents of the Combo Box is written back to the HighSchool Text file. This process Opens the file for Output, which destroys the contents. A new file is created using the contents of the Combo Box.

Private Sub Form_Unload(Cancel As Integer)
    Dim FN As Integer
    Dim i As Integer

    FN = FreeFile
    Open strFileSpec(FileName) For Output As FN
    For i = cboSchool.ListCount - 1 To 0 Step -1
        Print #FN, cboSchool.List(i)
    Next i
    Close FN

End Sub

This method is an effective way of maintaining the Text File. It is also possible to Append a record to a file each time a new High School is added to the Combo Box.


Random Access Files

A Random Access file is a sequential file of fixed sized records. This is Record I/O as opposed to string processing which was detailed in the previous sections. The advantage of random processing is that you have the ability to jump directly to any given record in the file. Each record consists of a series of fields and is defined as a User Defined Type (UDT) with fixed sized strings. In order to process the file randomly, you must supply the length of the records when the file is opened and you must supply a value indicating which record you want to access on the read and write statements.

Define your record using a UDT Type udtAddress
    aValid As String      * 1
    aName As String     * 20
    aAddress As String * 20
    aCSZ As String        * 30
    aPhone As String     * 15
End Type
Specify the record length
when the file is opened
Open strFileSpec For Random As #intFN Len = Len(udtMyAddress)

 

Get Get intFN, intRecNumber, udtMyAddress Retrieves the specified record from disk. The content is placed in the structure
Put Put intFN, intRecNumber, udtMyAddress Writes the contents of  structure to disk. The disk location is determined by the Record Number specified.

Data read with Get has usually been written to the file with Put.

Each time an I/O operation completes, the  current record position is changes to refer to the next sequential record in the file. The Record Number is optional and if it is omitted, the record will be determined by the current record position.

You can query or set the current record position using different forms of the Seek instruction.

Seek intFN, intPos Will change the Current Record Position to the record specified
Seek(intFN) Will return the Current Record Position

Because the record are fixed length, it is easy to determine how these functions locate the requested. The functions will position to a particular byte offset from within the file. This can be calculated using the formula:  (n-1) * Size of Record. This formula is never used within a program, but is used to explain how the I/O functions keep track of the current position in the file.

It is also a simple matter to determine how many records are in the file: RecordCount = LOF(intFN) / Len(IOArea)

It is not possible to physically delete a record from a Random Access file without rewriting every record in the file that follows the one to be deleted. The alternative is a logical delete, which is nothing more than a rewrite of the record with an indicator set that tell the program that the record is not to be processed.

When processing at the record level, you will need to write sunbroutines to move the data from the screen to the record and from the record to the screen.

Public Sub FileToScreen()
    With AddressRecord
        frmMain.txtName.Text = .aName
        frmMain.txtAddr.Text = .aAddress
        frmMain.txtCSZ.Text = .aCSZ
        frmMain.txtPhone.Text = .aPhone
    End With

End Sub
Public Sub ScreenToFile()
    With AddressRecord
        .aName = frmMain.txtName.Text
        .aAddress = frmMain.txtAddr.Text
        .aCSZ = frmMain.txtCSZ.Text
        .aPhone = frmMain.txtPhone.Text
    End With

End Sub


The Basic I/O functions involve reading a record, writing a record, and updating a record. The delete is a form of update that sets the Valid field to Deleted.

Public Function GetRecord(intKey As Integer) As Boolean
    Dim blnFound As Boolean

    Get #fnAddress, intKey, AddressRecord

    If AddressRecord.aValid = RecValid Then
        blnFound = True
    Else
        blnFound = False
    End If

    GetRecord = blnFound

End Function
Public Sub WriteRecord(intKey As Integer)
    Put #fnAddress, intKey, AddressRecord

End Sub

Private Sub cmdDelete_Click()
    Dim intKey As Integer

    intKey = Val(lblRecID.Caption)
    AddressRecord.aValid = RecDeleted

    WriteRecord intKey

End Sub


The Key to Random Access

In order for Random Access files to be effectively used, it is important to have a way to map a meaningful identifier (like Name) to the unmeaningful record number. There are many solutions to this problem, but none of them are particularly easy to program. The ListBox method of displaying the key (like Name) and storing the Record Number in the associated ItemData property is effective on a small scale. The collection also assists but as the collection is a class, much of the detail of the file access can be hidden.  

The example uses random access to Add, Update, and Delete records in a file. The delete is a logical delete as it is not possible to physically delete a record from a sequential file. The program also allows forward and backward browsing along with some logic to display the appropriate navigation buttons depending on the operation selected. This example also tries to simulate the features of database navigation that will be discussed next week.

Download the Example


Build the Example

This is the Collection Class Wrapper that contains the Random Access to a Person File.

Step by Step through the Example                        

It is important to note that the User Interface had almost no changes. Any change necessary to store the Person information on disk was made in the Collection Class Module.


Lab 1 -From the Text - Page 440, VB Mail Order

Step by Step through the First Lab

Let me know when you are done and I will check you off as Complete


Assignment

Reading Assignment

Programming Assignment

As Always: The assignment is due before the start of class next week

    Page 440, Problem 10.6

This assignment extends the assignment from Week 10
Put the Random Access File Handling in the Collection Class

Here is a list of the Program Grading Criteria