Reading Data from a Sequential-Access Text File
The previous section demonstrated how to create a file for use in sequential-access applications. In this section, we discuss how to read (or retrieve) data sequentially from a file.
Class ReadSequentialAccessFileForm (Fig. 18.11) reads records from the file created by the program in Fig. 18.9, then displays the contents of each record. Much of the code in this example is similar to that of Fig. 18.9, so we discuss only the unique aspects of the application.
When the user clicks the Open File button, the program calls event handler openButton_Click (lines 2050). Line 23 creates an OpenFileDialog, and line 24 calls its ShowDialog method to display the Open dialog (see the second screenshot in Fig. 18.11). The behavior and GUI for the Save and Open dialog types are identical, except that Save is replaced by Open. If the user inputs a valid file name, lines 4142 create a FileStream object and assign it to reference input. We pass constant FileMode.Open as the second argument to the FileStream constructor to indicate that the FileStream should open the file if it exists or should throw a FileNotFoundException if the file does not exist. (In this example, the FileStream constructor will not throw a FileNotFoundException, because the OpenFileDialog requires the user to enter a name of a file that exists.) In the last example (Fig. 18.9), we wrote text to the file using a FileStream object with write-only access. In this example (Fig. 18.11), we specify read-only access to the file by passing constant FileAccess.Read as the third argument to the FileStream constructor. This FileStream object is used to create a StreamReader object in line 45. The FileStream object specifies the file from which the StreamReader object will read text.
|
When the user clicks the Next Record button, the program calls event handler nextButton_Click (lines 5390), which reads the next record from the user-specified file. (The user must click Next Record after opening the file to view the first record.) Line 58 calls StreamReader method ReadLine to read the next record. If an error occurs while reading the file, an IOException is thrown (caught at line 85), and the user is notified (line 8788). Otherwise, line 61 determines whether StreamReader method ReadLine returned null (i.e., there is no more text in the file). If not, line 63 uses method Split of class string to separate the stream of characters that was read from the file into strings that represent the Record's properties. These properties are then stored by constructing a Record object using the properties as arguments (lines 6567). Line 70 displays the Record values in the TextBoxes. If ReadLine returns null, the program closes both the StreamReader object (line 74) and the FileStream object (line 75), then notifies the user that there are no more records (lines 8182).
Searching a Sequential-Access File
To retrieve data sequentially from a file, programs normally start from the beginning of the file, reading consecutively until the desired data is found. It sometimes is necessary to process a file sequentially several times (from the beginning of the file) during the execution of a program. A FileStream object can reposition its file-position pointer (which contains the byte number of the next byte to be read from or written to the file) to any position in the file. When a FileStream object is opened, its file-position pointer is set to byte position 0 (i.e., the beginning of the file)
We now present a program that builds on the concepts employed in Fig. 18.11. Class CreditInquiryForm (Fig. 18.12) is a credit-inquiry program that enables a credit manager to search for and display account information for those customers with credit balances (i.e., customers to whom the company owes money), zero balances (i.e., customers who do not owe the company money) and debit balances (i.e., customers who owe the company money for previously received goods and services). We use a RichTextBox in the program to display the account information. RichTextBoxes provide more functionality than regular TextBoxesfor example, RichTextBoxes offer method Find for searching individual strings and method LoadFile for displaying file contents. Classes RichTextBox and TextBox both inherit from abstract class System.Windows.Forms.TextBoxBase. We chose a RichTextBox in this example, because it displays multiple lines of text by default, whereas a regular TextBox displays only one. Alternatively, we could have specified that a TextBox object display multiple lines of text by setting its Multiline property to true.
The program displays buttons that enable a credit manager to obtain credit information. The Open File button opens a file for gathering data. The Credit Balances button displays a list of accounts that have credit balances, the Debit Balances button displays a list of accounts that have debit balances and the Zero Balances button displays a list of accounts that have zero balances. The Done button exits the application.
When the user clicks the Open File button, the program calls the event handler openButton_Click (lines 2455). Line 27 creates an OpenFileDialog, and line 28 calls its ShowDialog method to display the Open dialog, in which the user selects the file to open. Lines 4344 create a FileStream object with read-only file access and assign it to reference input. Line 47 creates a StreamReader object that we use to read text from the FileStream.
When the user clicks Credit Balances, Debit Balances or Zero Balances, the program invokes method getBalances_Click (lines 59119). Line 62 casts the sender parameter, which is an object reference to the control that generated the event, to a Button object. Line 65 extracts the Button object's text, which the program uses to determine which type of accounts to display. Line 71 uses FileStream method Seek to reset the file-position pointer back to the beginning of the file. FileStream method Seek allows you to reset the file-position pointer by specifying the number of bytes it should be offset from the file's beginning, end or current position. The part of the file you want to be offset from is chosen using constants from the SeekOrigin enumeration. In this case, our stream is offset by 0 bytes from the file's beginning (SeekOrigin.Begin). Lines 76111 define a while loop that uses private method ShouldDisplay (lines 122144) to determine whether to display each record in the file. The while loop obtains each record by repeatedly calling StreamReader method ReadLine (line 83) and splitting the text into tokens that are used to initialize object record (lines 8994). Line 86 determines whether the file-position pointer has reached the end of the file. If so, the program returns from method getBalances_Click (line 87).