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.