Visual Basic provides a number of commands for manipulating the filesystem. These commands are relatively flexible and easy to understand. Most of them have been around since the early days of Visual Basic, so many long-time Visual Basic developers prefer to use them rather than the newer .NET Framework methods.
One disadvantage to these methods is that they do not natively allow you to read and write nonstandard data types. They can handle string, date, integer, long, single, double, and decimal data. They can also handle structures and arrays of those types. They cannot, however, handle classes themselves. You can use XML serialization to convert a class object into a string and then use these methods to read and write the result, but that requires an extra step with some added complexity.
The section “File System Methods” later in this chapter describes the native filesystem methods of Visual Basic. The sections “Sequential-File Access,”“Random-File Access,” and “Binary-File Access” later in this chapter describe specific issues for working with sequential, random, and binary files.
The following table describes the methods Visual Basic provides for working with files:
METHOD | PURPOSE |
EOF | Returns True if a file open for reading is at the end of file. (EOF stands for End Of File.) |
FileClose | Closes an open file. |
FileGet | Reads data from a file opened in Random or Binary mode into a variable. |
FileGetObject | Reads data as an object from a file opened in Random or Binary mode into a variable. |
FileOpen | Opens a file for reading or writing. Parameters indicate the mode (Append, Binary, Input, Output, or Random), access type (Read, Write, or ReadWrite), and sharing (Shared, LockRead, LockWrite, or LockReadWrite). |
FilePut | Writes data from a variable into a file opened for Random or Binary access. |
FilePutObject | Writes an object from a variable into a file opened for Random or Binary access. |
FreeFile | Returns a file number that is not currently associated with any file in this application. You should use FreeFile to get file numbers rather than use arbitrary numbers such as 1. |
Input | Reads data written into a file by the Write method back into a variable. |
InputString | Reads a specific number of characters from the file. |
LineInput | Returns the next line of text from the file. |
Loc | Returns the current position within the file. |
LOF | Returns the file’s length in bytes. (LOF stands for Length Of File.) |
Prints values into the file. Multiple values separated by commas are aligned at tab boundaries. | |
PrintLine | Prints values followed by a new line into the file. Multiple values separated by commas are aligned at tab boundaries. |
Seek | Moves to the indicated position within the file. |
Write | Writes values into the file, delimited appropriately so that they can later be read by the Input method. |
WriteLine | Writes values followed by a new line into the file, delimited appropriately so that they can later be read by the Input method. |
Many of the Visual Basic file methods use a file number to represent an open file. The file number is just a number used to identify the file. There’s nothing magic about it. You just need to be sure not to use the same file number for more than one file at the same time. The FreeFile method returns a number that is not in use so that you know it is safe to use as a file number.
The following example uses FreeFile to get an available file number. It uses FileOpen to open a file for reading. Then, while the EOF method indicates that the code hasn’t reached the end of the file, the program uses LineInput to read a line from the file and it displays the line. When it finishes reading the file, the program uses FileClose to close it.
' Get an available file number.
Dim file_num As Integer = FreeFile()
' Open the file.
FileOpen(file_num, "C:Temp est.txt",
OpenMode.Input, OpenAccess.Read, OpenShare.Shared)
' Read the file's lines.
Do While Not EOF(file_num)
' Read a line.
Dim txt As String = LineInput(file_num)
Debug.WriteLine(txt)
Loop
' Close the file.
FileClose(file_num)
Visual Basic also provides several methods for working with the filesystem. The following table describes methods that manipulate directories and files:
METHOD | PURPOSE |
ChDir | Changes the application’s current working directory. |
ChDrive | Changes the application’s current working drive. |
CurDir | Returns the application’s current working directory. |
Dir | Returns a file matching a directory path specification that may include wildcards, and matching certain file properties such as ReadOnly, Hidden, or Directory. The first call to Dir should include a path. Subsequent calls can omit the path to fetch the next matching file for the same initial path. Dir returns filenames without the path and returns Nothing when no more files match. |
FileCopy | Copies a file to a new location. |
FileDateTime | Returns the date and time when the file was created or last modified. |
FileLen | Returns the length of a file in bytes. |
GetAttr | Returns a value indicating the file’s attributes. The value is a combination of the values vbNormal, vbReadOnly, vbHidden, vbSystem, vbDirectory, vbArchive, and vbAlias. |
Kill | Permanently deletes a file. |
MkDir | Creates a new directory. |
Rename | Renames a directory or file. |
RmDir | Deletes an empty directory. |
SetAttr | Sets the file’s attributes. The attribute value is a combination of the values vbNormal, vbReadOnly, vbHidden, vbSystem, vbDirectory, vbArchive, and vbAlias. |
With sequential-file access, a program reads or writes the contents of a file byte by byte from start to finish with no jumping around. In contrast, in a random-access file, the program can jump freely to any position in the file and write data wherever it likes.
A text file is a typical sequential file. The program can read the text in order, and read it one line at a time, but it cannot easily jump around within the file.
The Input, InputString, LineInput, Print, PrintLine, Write, and WriteLine methods provide sequential access to files.
The Print and PrintLine methods provide mostly unformatted results. If you pass these methods multiple parameters separated by commas, they align the results on tab boundaries. Write and WriteLine, on the other hand, delimit their output so that it can be easily read by the Input method.
A program cannot directly modify only part of a sequential file. For example, it cannot modify, add, or remove a sentence in the middle of a paragraph. If you must modify the file, you should read it into a string, make the changes you want, and then rewrite the file.
If you must frequently modify text in the middle of a file, you should consider using random or binary access, or storing the data in a database.
A random-access file contains a series of fixed-length records. For example, you could create an employee file that contains a series of values defining an employee. Each record would have fixed-length fields to hold an employee’s ID, first name, last name, street address, and so forth, as shown in the following structure definition:
Structure Employee
Public Id As Long
<VBFixedString(20)> Public FirstName As String
<VBFixedString(20)> Public LastName As String
<VBFixedString(40)> Public Street As String
...
End Structure
When you open a file for random access, you can jump to any record in the file. That makes certain kinds of file manipulation easier. For example, if the file is sorted, you can use a binary search to locate records in it.
You can overwrite the values in a record within the file, but you cannot add or remove records in the middle of the file. If you must make those sorts of changes, you must load the file into memory and then rewrite it from scratch.
The FileGet, FileGetObject, FilePut, and FilePutObject methods read and write records in random-access files. Example program RandomAccessEmployees uses the following code to demonstrate the FilePut and FileGet methods:
Public Class Form1
Public Structure Employee
Public ID As Integer
<VBFixedString(15) > Public FirstName As String
<VBFixedString(15) > Public LastName As String
Public Sub New(new_id As Integer, first_name As String,
last_name As String)
ID = new_id
FirstName = first_name
LastName = last_name
End Sub
Public Overrides Function ToString() As String
Return ID & ": " & FirstName & " " & LastName
End Function
End Structure
Private Sub btnMakeRecords_Click() Handles btnMakeRecords.Click
' Declare a record variable.
Dim emp As New Employee
' Get an available file number.
Dim file_num As Integer = FreeFile()
' Open the file.
FileOpen(file_num, "MYFILE.DAT", OpenMode.Random,
OpenAccess.ReadWrite, OpenShare.Shared, Len(emp))
' Make some records.
FilePut(file_num, New Employee(1, "Alice", "Altanta"))
FilePut(file_num, New Employee(2, "Bob", "Bakersfield"))
FilePut(file_num, New Employee(3, "Cindy", "Chicago"))
FilePut(file_num, New Employee(4, "Dan", "Denver"))
FilePut(file_num, New Employee(5, "Erma", "Eagle"))
FilePut(file_num, New Employee(6, "Fred", "Frisco"))
' Fetch and display the records.
Dim obj As ValueType = DirectCast(emp, ValueType)
For Each i As Integer In New Integer() {3, 1, 5, 2, 6}
FileGet(file_num, obj, i)
emp = DirectCast(obj, Employee)
Debug.WriteLine("[" & emp.ToString() & "]")
Next i
' Close the file.
FileClose(file_num)
End Sub
End Class
First, the code defines a structure named Employee to hold the data in a record. Notice how the code uses the VBFixedString attribute to flag the strings as fixed length. The structure must have a fixed length if you want to jump randomly through the file because Visual Basic calculates a record’s position by multiplying a record’s size by its index in the file. If records contained strings of unknown length, the calculation wouldn’t work.
When the user clicks the Make Records button, the btnMakeRecords_Click event handler executes. This code declares a variable of the record type, Employee. It uses the FreeFile method to get an available file number and uses FileOpen to open the file for random access. The final parameter to FileOpen is the length of the file’s records. To calculate this length, the program uses the Len function, passing it the Employee instance emp.
Next, the program uses the FilePut method to write six records into the file. It passes FilePut the file number and a new Employee structure. The structure’s constructor makes initializing the new records easy.
The program then uses FileGet to retrieve the six records using their indexes as keys, fetching them out of numeric order to demonstrate random access. It then displays each record’s data in the Output window surrounded by brackets so you can see where the data starts and ends.
There are two key points to notice here. First, the file numbers records starting with 1 not 0, so the first record in the file has index 1.
Second, the FileGet method does not have an overloaded version that takes an Employee structure as a parameter. Because this example has Option Strict set to On, the code must pass FileGet a ValueType variable and then convert it into an Employee.
If you set Option Strict to Off, you can pass an Employee variable directly into FileGet.
After it has read and displayed the records, the program uses FileClose to close the file.
The following text shows the result. Notice that the first and last names are padded with spaces to 15 characters, the length of the Employee structure’s fixed-length strings. The last names are also padded to 15 characters.
[3: Cindy Chicago ]
[1: Alice Altanta ]
[5: Erma Eagle ]
[2: Bob Bakersfield ]
[6: Fred Frisco ]
Binary access is similar to random access, except that it does not require its data to fit into neat records. You get control over pretty much every byte in the file, and you can jump to an arbitrary byte number in the file. If the items in the file are not fixed-length records, however, you cannot jump to a particular record because you cannot calculate where that record would begin.