Let’s look at the code for saving structured information to a binary file. In this section, you’ll build the RecordSave application, which demonstrates how to store a price list to a disk file and read it later from the same file. The main form of the application is shown in Figure 11.2. The Save Records button creates a few records and then saves them to disk. The Read Records button reads the records from the file and displays them in the ListBox control.
Each record of the price list contains the following fields:
- The product’s ID (a String)
- The product’s description (a String)
- The product’s price (a Single value)
- The product’s availability (a Boolean value)
- The minimum reorder quantity (an Integer value)
The program saves each field as a separate entity, using the Write method of the BinaryStream class. Only the string is written to the file with the WriteString method because we want to be able to read the string back with the ReadString method.
Because the price list contains many products, you will most likely store it in an array of custom structures. The Product structure shown next is a simple, yet quite adequate, structure for our price list:
Code language: PHP (php)
Structure Product Dim ProdID As String Dim prodDescription As String Dim listPrice As Single Dim available As Boolean Dim minStock As Integer End Structure
The code that writes the structure to a binary file is shown in Listing 11.13.
Listing 11.13: Saving a Record to a Binary File
Code language: PHP (php)
Private Sub bttnSave Click(...) Handles bttnSave.Click Dim BW As BinaryWriter Dim FS As FileStream FS = New FileStream("Records.bin", System.IO.FileMode.OpenOrCreate, _ System.IO.FileAccess.Write) BW = New BinaryWriter(FS) BW.BaseStream.Seek(0, SeekOrigin.Begin) Dim p As New Product() ' Save first record p.ProdID = "100-A39" p.prodDescription = "Cellular Phone with built-in TV" p.listPrice = 497.99 p.available = True p.minStock = 40 SaveRecord(BW, p) ' Save second record p = New Product() p.ProdID = "100-U300" p.prodDescription = "Wireless Handheld" p.listPrice = 315.5 p.available = False p.minStock = 12 SaveRecord(BW, p) ' Save third record p = New Product() p.ProdID = "ZZZ" p.prodDescription = "Last Gadget" p.listPrice = .99 p.available = True p.minStock = 1000 SaveRecord(BW, p) BW.Close() FS.Close() End Sub
The code of the SaveRecord() subroutine is shown in Listing 11.14. It accepts as arguments the BinaryWriter class that represents the binary file to which the data will be written and a Product structure to be saved to the file.
Listing 11.14: SaveRecord() Subroutine
Code language: CSS (css)
Sub SaveRecord(ByVal writer As BinaryWriter, ByVal record As Product) writer.Write(record.ProdID) writer.Write(record.prodDescription) writer.Write(record.listPrice) writer.Write(record.available) writer.Write(record.minStock) End Sub
To read the records stored in the file, set up a BinaryReader associated with the Records.bin file and call the appropriate Read method for each field of the record. Because we don’t know in advance how many records are in the file, we set up a loop that keeps reading one record at a time, while the current position (property Position of the FileStream object) is less than the length of the file (property Length of the FileStream object). Listing 11.15 is the code behind the Read Records button.
Listing 11.15: Reading Records from a Binary File
Code language: PHP (php)
Private Sub bttnRead Click(...) Handles bttnRead.Click Dim BR As BinaryReader Dim FS As FileStream FS = New System.IO.FileStream("Records.bin", FileMode.Open, _ FileAccess.Read) BR = New System.IO.BinaryReader(FS) BR.BaseStream.Seek(0, SeekOrigin.Begin) Dim p As New Product() TextBox1.Clear() Dim c As Integer c = BR.PeekChar While FS.Position < FS.Length p = Nothing ' Read fields and populate structure p.ProdID = BR.ReadString p.prodDescription = BR.ReadString p.listPrice = BR.ReadSingle p.available = BR.ReadBoolean p.minStock = BR.ReadInt32 ' Display structure ShowRecord(p) c = BR.PeekChar End While BR.Close() FS.Close() End Sub
Notice that the product’s price is read with the ReadSingle method because it was saved as a Single variable. The ShowRecord() subroutine appends the fields of the current structure to the TextBox control at the bottom of the form.
Using a custom structure to store the fields simplifies the structure of the application at large, but it doesn’t help the file I/O operation much. It’s quicker to use the Serializer class to store an entire collection to the file at once, rather than each member of the collection individually. The Serializer class, which is discussed in detail in Chapter 16, addresses many of the file I/O needs of your applications. There will be situations, however, in which you must store widely different pieces of information to a text or binary file, and the information presented in this chapter should be adequate for these situations.
Reading Legacy Data with the FileSystem Object
A convenient feature of the My.Computer.FileSystem component is the OpenTextFieldParser method, which reads the fields of a delimited or fixed-width text file. These files are created by many applications when they export their data in text format. The ParseLegacyData project demonstrates how to use the FileSystem object, and the OpenTextFieldParser method in specific, to read legacy data. In the project’s folder you’ll find a Readme file that explains the process in detail.