Random Files

 

Random files are record-based files with an internal structure that supports "direct access" by record number. This means that your program can read from or write to a specific record in a random access file, say the 50th record, without reading through the previous 49 records. Compare that to reading or writing a sequential file, where to get to a specific record, you must read through all preceding records.

 

The difference between random access and sequential access can be likened to accessing music on a CD versus a cassette tape. To get to song number 6, you can tell your CD player to go directly to track 6, whereas on a cassette tape, you must fast-forward through the first 5 songs to get to song number 6.

 

In the earlier days of BASIC, before the "client-server era" where RAD systems such as VB, Delphi, and PowerBuilder interacted with desktop and ODBC databases such as MS-Access, SQL Server, and Oracle, random access files were used as building blocks to put together data access systems that could be considered early forms of desktop databases.

 

FileOpen Function for Random Files

 

We have seen this function in detail in previous examples. Let us now see how to use this with Random files.

The following example opens the file in Random mode. The file contains records of the structure Person. Please note that for creating fixed length string in VB.NET, we use VBFixedString attribute.

Structure Person
   <VBFixedString(30)> Dim Name As String
   Dim ID As Integer
End Structure
 
' Count 30 for the string, plus 4 for the integer.
FileOpen(1, "TESTFILE", OpenMode.Random, , , 34)
' Close before reopening in another mode.
FileClose(1)

 

The sample program for this topic will use a random access version of the employee file we used in the topics on sequential files. The Type structure for the employee record will be defined as follows:

 

    Private Structure EmployeeRecord

        <VBFixedString(20)> Dim EmpName As String

        Dim DeptNbr As Integer

        <VBFixedString(25)> Dim JobTitle As String

        Dim HireDate As Date

        Dim HrlyRate As Single

    End Structure

 

The record variable based on this EmployeeRecord Type will be defined as:

 

    Private mudtEmpRecord As EmployeeRecord

 

Note that the String variables that make up this structure are defined as fixed-length strings. This is important for efficient access of the random file. Given that, the size of an Integer is 4, the size of a Date is 8, and the size of a Single is 4, the total length of the structure above is 61 (20 + 4 + 25 + 8 + 4)

Open statement for the random employee file could be written as:

 

FileOpen(intRndEmpFileNbr, strRndEmpFileName, OpenMode.Random, OpenAccess.Write, , Len(mudtEmpRecord))

 

In the syntax above, the Len function is used on the record variable, thus "letting the computer do the work" of figuring out the total length of the structure. And, using this method, if we add fields to or remove fields from the structure, we don't have to worry about recalculating the total length.

 

The usage of FileGet and FilePut functions is same as discussed in the previous lesson on Binary Files.

 

Sample Program

 

The sample program performs three main functions that demonstrate the features of random files: (1) creates a random file based on input from a sequential file; (2) reads back the random file just created and displays its contents; and (3) retrieves a record from the random file given the record number, and updates a field in the retrieved record based on user input.

 

The code listed below is heavily commented to aid in the understanding of how the program works.

 

Code:

 

Module Module1

 

    Private Structure EmployeeRecord

        <VBFixedString(20)> Dim EmpName As String

        Dim DeptNbr As Integer

        <VBFixedString(25)> Dim JobTitle As String

        Dim HireDate As Date

        Dim HrlyRate As Single

    End Structure

 

    Private mudtEmpRecord As EmployeeRecord

 

    Public Sub Main()

 

        Dim strSeqEmpFileName As String

        Dim strRndEmpFileName As String

        Dim intSeqEmpFileNbr As Integer

        Dim intRndEmpFileNbr As Integer

 

        Dim strNewJob As String

 

        Dim intRecordCount As Integer

        Dim strRecordNumber As String

        Dim intRecordNumber As Integer

        Dim intX As Integer

 

        Dim strEmpName As String

        Dim intDeptNbr As Integer

        Dim strJobTitle As String

        Dim dtmHireDate As Date

        Dim sngHrlyRate As Single

 

        strSeqEmpFileName = My.Application.Info.DirectoryPath & "\EMPLOYEE.txt"

        strRndEmpFileName = My.Application.Info.DirectoryPath & "\EMPLOYEE.RND"

 

        '-----------------------------------------------------------------------

        ' In the first part of this sample program, we will create, or load,

        ' a random access version of the comma-delimited sequential employee

        ' file that was used in one of the sample programs for sequential access

        ' files.

        '-----------------------------------------------------------------------

 

        ' Open the sequential employee file for input ...

        intSeqEmpFileNbr = FreeFile()

        FileOpen(intSeqEmpFileNbr, strSeqEmpFileName, OpenMode.Input)

 

        ' If the random employee file we want to write already exists,

        ' delete it ...

        If Dir(strRndEmpFileName) <> "" Then

            Kill(strRndEmpFileName)

        End If

 

        ' Open the random employee for writing ...

        intRndEmpFileNbr = FreeFile()

        FileOpen(intRndEmpFileNbr, _

                 strRndEmpFileName, _

                 OpenMode.Random, _

                 OpenAccess.Write, _

                 , _

                 Len(mudtEmpRecord))

 

        ' Initialize record count variable to keep track of how many records will

        ' be written to the random file ...

        intRecordCount = 0

 

        ' This loop will read a record from the comma-delimited sequential employee file

        ' and write a corresponding record to its random access counterpart ...

        Do Until EOF(intSeqEmpFileNbr)

            ' Read a record's worth of fields from the comma-delimited employee file,

            ' storing the fields into their corresponding variables ...

            Input(intSeqEmpFileNbr, strEmpName)

            Input(intSeqEmpFileNbr, intDeptNbr)

            Input(intSeqEmpFileNbr, strJobTitle)

            Input(intSeqEmpFileNbr, dtmHireDate)

            Input(intSeqEmpFileNbr, sngHrlyRate)

            ' Assign each variable read in from the comma-delimited file to its corresponding

            ' field in the mudtEmpRecord record variable (based on the EmployeeRecord UDT).

            ' Note that a With/End With block is used. If With/End With was not used, this set

            ' of assignment statements would have to be written as follows:

            '            mudtEmpRecord.EmpName = strEmpName

            '            mudtEmpRecord.DeptNbr = intDeptNbr

            '            mudtEmpRecord.JobTitle = strJobTitle

            '            mudtEmpRecord.HireDate = dtmHireDate

            '            mudtEmpRecord.HrlyRate = sngHrlyRate

            With mudtEmpRecord

                .EmpName = strEmpName

                .DeptNbr = intDeptNbr

                .JobTitle = strJobTitle

                .HireDate = dtmHireDate

                .HrlyRate = sngHrlyRate

            End With

            ' Now that the record variable has been populated with the proper data,

            ' write the record out to the random file using the FilePut function...

 

            FilePut(intRndEmpFileNbr, mudtEmpRecord)

 

            ' Increment the record count variable ...

            intRecordCount = intRecordCount + 1

        Loop

 

        ' Close the sequential file and the random file ...

        FileClose(intSeqEmpFileNbr)

        FileClose(intRndEmpFileNbr)

 

        '-----------------------------------------------------------------------

        ' In the next part of this sample program, we will display the records

        ' written to the random file by reading them back and printing their

        ' contents one by one.

        '-----------------------------------------------------------------------

 

        ' Print headings ...

        Console.WriteLine("{0} employee records were written to the random file.", intRecordCount)

        Console.WriteLine()

        Console.WriteLine("Contents as follows:")

        Console.WriteLine()

 

        Console.WriteLine("EMP NAME".PadRight(20) & " " & _

                          "DEPT".PadRight(4) & " " & _

                          "JOB TITLE".PadRight(25) & " " & _

                          "HIRE DATE".PadRight(10) & " " & _

                          "HRLY RATE".PadRight(7))

 

        Console.WriteLine("--------".PadRight(20) & " " & _

                          "----".PadRight(4) & " " & _

                          "---------".PadRight(25) & " " & _

                          "---------".PadRight(10) & " " & _

                          "---------".PadRight(7))

 

        ' Open the random file for reading ...

        intRndEmpFileNbr = FreeFile()

        FileOpen(intRndEmpFileNbr, strRndEmpFileName, OpenMode.Random, OpenAccess.Read, , Len(mudtEmpRecord))

 

        ' Since we know how many records are in the file, we can use a For/Next loop

        ' to control the reading and printing of the records ...

        For intX = 1 To intRecordCount

            ' With the Get statement, read the next (or first) record from the

            ' random file, storing its contents in the mudtEmpRecord structure ...

            FileGet(intRndEmpFileNbr, mudtEmpRecord)

            ' Print the data from the record. Once again, a With/End With

            ' block is used to "factor out" the record variable.

 

            With mudtEmpRecord

                Console.WriteLine(.EmpName.PadRight(20) & " " & _

                                  .DeptNbr.ToString.PadLeft(4) & " " & _

                                  .JobTitle.PadRight(25) & " " & _

                                  Format(.HireDate, "MM/dd/yyyy").PadRight(10) & " " & _

                                  Format(.HrlyRate, "Standard").PadLeft(7))

            End With

        Next

        Console.WriteLine()

        ' Close the random file ...

        FileClose(intRndEmpFileNbr)

 

        '-----------------------------------------------------------------------

        ' In the last part of this sample program, we will request an employee

        ' record and then update the job title for that employee.

        '-----------------------------------------------------------------------

 

        ' Prompt the user to enter a valid record number (the record number must be

        ' between 1 and the number of records in the file). The loop below validates

        ' the entry, and re-prompts the user if necessary, before moving on ...

        Do

            Console.Write("Enter a record number between 1 and {0}: ", intRecordCount)

            strRecordNumber = Console.ReadLine()

            If strRecordNumber = "" Then Exit Sub

            intRecordNumber = Val(strRecordNumber)

            If (intRecordNumber < 1) Or (intRecordNumber > intRecordCount) Then

                Console.WriteLine("Invalid Record Number.")

            End If

        Loop Until intRecordNumber >= 1 And intRecordNumber <= intRecordCount

 

        ' Open the random employee file for read/write access ...

        intRndEmpFileNbr = FreeFile()

        FileOpen(intRndEmpFileNbr, _

                 strRndEmpFileName, _

                 OpenMode.Random, _

                 OpenAccess.ReadWrite, _

                 , _

                 Len(mudtEmpRecord))

 

        ' Get the employee record corresponding to the record number entered above ...

        FileGet(intRndEmpFileNbr, mudtEmpRecord, intRecordNumber)

 

        ' Prompt the user to enter a new job title for the employee ...

        Console.WriteLine("The employee in record # {0} is {1}.", _

                          intRecordNumber, Trim(mudtEmpRecord.EmpName))

        Console.Write("Enter the new job title for this employee: ")

        strNewJob = Console.ReadLine()

 

        ' Display the results

        Console.WriteLine()

        If strNewJob = "" Then

            Console.WriteLine("Record was not updated.")

        Else

            mudtEmpRecord.JobTitle = strNewJob

            FilePut(intRndEmpFileNbr, mudtEmpRecord, intRecordNumber)

            Console.WriteLine("Job title for {0} was updated to {1}.", _

                              Trim(mudtEmpRecord.EmpName), Trim(mudtEmpRecord.JobTitle))

        End If

 

        ' Close the random file ...

        FileClose(intRndEmpFileNbr)

        Console.ReadLine()

 

    End Sub

 

End Module

 

Screenshots of the run are shown below:

 

First, the records that were written to the random file are displayed, and the user is prompted to enter a record number:

 

The user enters 3, which the system identifies as the record for "CHARLIE CHEESEMAN", and the user is prompted to enter a new job title.

 

The user enters "SR. COMPUTER OP.", and the program verifies the change:

 

Note: If you run this program subsequent times as-is, you will see that any changes you make will not seem to "stick". This is because the program as-is recreates the random file every time it is run. To see your changes "stick", you would have to comment-out the portion of the program that recreates the random file from the "employee.txt" input file.

 

Download the VB project code for the example above here.