Random-Access Files

So far, we have seen how to create and manipulate sequential-access files. Sequential-access files are inappropriate for so-called instant-access applications, in which the desired information must be located immediately. Some popular instant-access applications are airline-reservation systems, banking systems, point-of-sale systems, automated teller machines (ATMs) and other kinds of transaction-processing systems that require rapid access to specific data. The bank at which you have your account may have hundreds of thousands, or even millions, of other customers, but when you use an ATM, the bank determines in seconds whether your account has sufficient funds for the transaction. This kind of instant access is possible with random-access files and with databases (Chapter 25). A program can access individual records of a random-access file directly (and quickly) without searching through other records. Random-access files are sometimes called direct-access files.

Recall from Section 14.5 that Java does not impose structure on a file, so an application that wants to use random-access files must specify the format of those files. Several techniques can be used to create random-access files. Perhaps the simplest is to require that all the records in a file be of the same fixed length. Using fixed-length records makes it easy for a program to calculate (as a function of the record size and the record key) the exact location of any record relative to the beginning of the file. We soon see how this capability facilitates direct access to specific records, even in large files.

Figure 14.22 illustrates Java's view of a random-access file composed of fixed-length records. (Each record in this figure is 100 bytes long.) A random-access file is like a railroad train with many carssome empty, some with contents.

Figure 14.22. Java's view of a random-access file.

A program can insert data in a random-access file without destroying the other data in the file. Similarly, a program can update or delete data stored previously without rewriting the entire file. In the following sections, we explain how to create a random-access file, enter data, read the data both sequentially and randomly, update the data and delete the data.

14.7.1. Creating a Random-Access File

A RandomAccessFile is useful for direct-access applications. With a sequential-access file, each successive input/output request reads or writes the next consecutive set of data in the file. With a random-access file, each successive input/output request could be directed to any part of the fileperhaps a section widely separated from the part referenced in the previous request. Direct-access applications provide rapid access to specific data items in large files, but users often have to wait for answers. In many instances, however, answers must be made available quickly, to prevent people from becoming impatient and taking their business elsewhere.

RandomAccessFile objects have all the capabilities of classes FileInputStream and FileOutputStream, as well as the capabilities described by interfaces DataInput and DataOutput. These interfaces provide methods for reading and writing primitive-type values, byte arrays and strings. When a program associates an object of class RandomAccessFile with a file, the program reads or writes data, beginning at the location in the file specified by the file-position pointer (the byte number of the next byte in the file to be read or written to), and manipulates all the data as primitive types. When writing an int value, four bytes are output to the file. When reading a double value, eight bytes are input from the file. The size of the types is guaranteed, because Java has fixed representations and sizes for all primitive types, regardless of the computing platform.

Random-access file-processing programs rarely write a single field to a file. Normally they write one object at a time, as we show in the upcoming examples. Consider the following problem:

Create a transaction-processing program capable of storing up to 100 fixed-length records for a company that can have up to 100 customers. Each record should consist of an account number (that will be used as the record key), a last name, a first name and a balance. The program should be able to update an account, insert a new account and delete an account.

The next several sections introduce the techniques necessary to create this credit-processing program. Figure 14.23 contains the RandomAccessAccountRecord class that is used by the next four programs for both reading records from and writing records to a file. Class RandomAccessAccountRecord inherits our AccountRecord implementation (Fig. 14.6), which includes private fieldsaccount, lastName, firstName and balanceas well as set and get methods for each field. We could inherit from either AccountRecord or AccountRecordSerializable. We do not use object serialization when processing random-access files in this chapter, so we inherit from class AccountRecord. Note that the class is in package com.deitel.jhtp6.ch14.

Figure 14.23. RandomAccessAccountRecord class used in random-access file programs.

(This item is displayed on pages 708 - 709 in the print version)

 1 // Fig. 14.23: RandomAccessAccountRecord.java
 2 // Subclass of AccountRecord for random-access file programs.
 3 package com.deitel.jhtp6.ch14; // packaged for reuse
 4
 5 import java.io.RandomAccessFile;
 6 import java.io.IOException; 
 7
 8 public class RandomAccessAccountRecord extends AccountRecord
 9 {
10 public static final int SIZE = 72;
11
12 // no-argument constructor calls other constructor with default values
13 public RandomAccessAccountRecord()
14 {
15 this ( 0, "", "", 0.0 );
16 } // end no-argument RandomAccessAccountRecord constructor
17
18 // initialize a RandomAccessAccountRecord
19 public RandomAccessAccountRecord( int account, String firstName,
20 String lastName, double balance )
21 {
22 super( account, firstName, lastName, balance );
23 } // end four-argument RandomAccessAccountRecord constructor
24
25 // read a record from specified RandomAccessFile
26 public void read( RandomAccessFile file ) throws IOException
27 {
28 setAccount( file.readInt() );
29 setFirstName( readName( file ) );
30 setLastName( readName( file ) );
31 setBalance( file.readDouble() );
32 } // end method read
33
34 // ensure that name is proper length
35 private String readName( RandomAccessFile file ) throws IOException
36 {
37 char name[] = new char[ 15 ], temp;
38
39 for ( int count = 0; count < name.length; count++ )
40 {
41 temp = file.readChar();
42 name[ count ] = temp;
43 } // end for
44
45 return new String( name ).replace( '', ' ' );
46 } // end method readName
47
48 // write a record to specified RandomAccessFile
49 public void write( RandomAccessFile file ) throws IOException
50 {
51 file.writeInt( getAccount() );
52 writeName( file, getFirstName() );
53 writeName( file, getLastName() );
54 file.writeDouble( getBalance() );
55 } // end method write
56
57 // write a name to file; maximum of 15 characters
58 private void writeName( RandomAccessFile file, String name )
59  throws IOException 
60 {
61 StringBuffer buffer = null;
62
63 if ( name != null )
64 buffer = new StringBuffer( name );
65 else
66 buffer = new StringBuffer( 15 );
67
68 buffer.setLength( 15 );
69 file.writeChars( buffer.toString() );
70 } // end method writeName
71 } // end class RandomAccessAccountRecord

Finally, this example also introduces class StringBuffer, a class that allows us to dynamically manipulate strings. Class String provides many capabilities for processing strings. However, String objects are immutabletheir character contents cannot be changed after they are created. Class StringBuffer provides features for creating and manipulating dynamic string informationthat is, modifiable strings. Every StringBuffer is capable of storing a number of characters specified by its capacity. If the capacity of a StringBuffer is exceeded, the capacity is expanded to accommodate the additional characters. We use class StringBuffer to specify the size of a person's first or last name. We discuss class StringBuffer in more detail in Section 14.8, as well as in Chapter 29, Strings, Characters and Regular Expressions.

Line 10 declares the constant SIZE to represent the size, in bytes, of a record. A RandomAccessAccountRecord contains an int (4 bytes), two strings that we restrict to 15 characters each (30 bytes for the first name, 30 bytes for the last name) for this example and a double (8 bytes), for a total of 72 bytes.

Method read (lines 2632) reads one record from the RandomAccessFile specified as its argument. RandomAccessFile methods readInt (line 28) and readDouble (line 31) read the account and balance, respectively. Lines 2930 call utility method readName (lines 3546) twice to obtain the first and last names. Method readName reads 15 characters from the RandomAccessFile and returns a String. If a name is shorter than 15 characters, the extra characters have the default value ''the default for a char. Swing GUI components, such as JTextFields, cannot display null-byte charactersinstead, they display them as rectangles. Line 45 solves this problem by using String method replace to replace null bytes with spaces. Although our program does not use a GUI, we added this capability for those who wish to reuse this class in a GUI program.

Method write (lines 4955) outputs one record to the RandomAccessFile specified as its argument. This method uses RandomAccessFile method writeInt to output the integer account, method writeChars (called from utility method writeName in line 69) to output the firstName and lastName character arrays, and method writeDouble to output the double balance. [Note: To ensure that all the records in the RandomAccessFile are the same size, we write exactly 15 characters for the first name and exactly 15 for the last name.] Method writeName (lines 5870) performs the write operations for the first and last names. Lines 6366 create a StringBuffer that is initialized with either the name specified as an argument or with 15 to specify the size of the StringBuffer if name is null. Line 68 sets the number of characters in the StringBuffer. If a name is longer than 15 characters, it will be truncated to 15 characters. If a name is smaller than 15 characters it will be set to have 15 characters, with null characters ('') to fill the extra space.

Figure 14.24Fig. 14.25 illustrates the process of opening a random-access file and writing data to the disk. This program writes 100 blank RandomAccessAccountRecords. Each RandomAccessAccountRecord object contains 0 for the account number, null for the last name, null for the first name and 0.0 for the balance. The file is initialized to create the proper amount of "empty" space in which the account data will be stored and to enable us to determine in subsequent programs whether each record is empty or contains data.

Figure 14.24. Random-access file created sequentially.

(This item is displayed on page 711 in the print version)

 1 // Fig. 14.24: CreateRandomFile.java
 2 // Creates random-access file by writing 100 empty records to disk.
 3 import java.io.IOException;
 4 import java.io.RandomAccessFile;
 5
 6 import com.deitel.jhtp6.ch14.RandomAccessAccountRecord;
 7
 8 public class CreateRandomFile
 9 {
10 private static final int NUMBER_RECORDS = 100;
11
12 // enable user to select file to open
13 public void createFile()
14 {
15 RandomAccessFile file = null;
16
17 try // open file for reading and writing
18 {
19 file = new RandomAccessFile( "clients.dat", "rw" );
20
21 RandomAccessAccountRecord blankRecord =
22 new RandomAccessAccountRecord();
23
24 // write 100 blank records
25 for ( int count = 0 ; count < NUMBER_RECORDS; count++ )
26 blankRecord.write( file );
27
28 // display message that file was created
29 System.out.println( "Created file clients.dat." );
30
31 System.exit( 0 ); // terminate program
32 } // end try
33 catch ( IOException ioException )
34 {
35 System.err.println( "Error processing file." );
36 System.exit( 1 );
37 } // end catch
38 finally
39 {
40 try
41 {
42 if ( file != null )
43 file.close(); // close file
44 } // end try
45 catch ( IOException ioException )
46 {
47 System.err.println( "Error closing file." );
48 System.exit( 1 );
49 } // end catch
50 } // end finally
51 } // end method createFile
52 } // end class CreateRandomFile

Figure 14.25. Testing class CreateRandomFile.

(This item is displayed on page 712 in the print version)

 1 // Fig. 14.25: CreateRandomFileTest.java
 2 // Testing class CreateRandomFile.
 3
 4 public class CreateRandomFileTest
 5 {
 6 public static void main( String args[] )
 7 {
 8 CreateRandomFile application = new CreateRandomFile();
 9 application.createFile();
10 } // end main
11 } // end class CreateRandomFileTest
 
Created file clients.dat.
 

Line 19 of Fig. 14.24 attempts to open a RandomAccessFile for use in this program. The RandomAccessFile constructor accepts two argumentsthe file name and the file-open mode. The file-open mode for a RandomAccessFile is either "r" (to open the file for reading) or "rw" (to open the file for reading and writing). Once again, we have used a new file extension (.dat). We use this file extension for binary files that do not use object serialization.

If an IOException occurs while opening the file, the program displays a message and terminates. If the file opens properly, lines 2526 invoke RandomAccessAccountRecord method write 100 times. This method causes the fields of object blankRecord to be written to the file associated with RandomAccessFile object file. Then line 43 closes the file. The code for closing the file is placed in it's own try statementif an attempt to close the file generates an IOException, this exception is caught in lines 4549. Figure 14.25 begins the execution of the program with method main (lines 610). Line 8 creates a CreateRandomFile object and line 9 calls its createFile method to create the file of 100 blank records.

14.7.2. Writing Data Randomly to a Random-Access File

The application in Fig. 14.26Fig. 14.27 writes data to a file that is opened with the "rw" mode (for reading and writing). It uses RandomAccessFile method seek to position to the exact location in the file at which a record of information is stored. Method seek sets the file-position pointer to a specific location in the file relative to the beginning of the file, and RandomAccessAccountRecord method write outputs the data at the current position in the file. The program assumes that the user does not enter duplicate account numbers.

Figure 14.26. Writing data to a random-access file.

(This item is displayed on pages 712 - 714 in the print version)

 1 // Fig. 14.26: WriteRandomFile.java
 2 // This program retrieves information from the user at the
 3 // keyboard and writes the information to a random-access file.
 4 import java.io.File;
 5 import java.io.IOException;
 6 import java.io.RandomAccessFile;
 7 import java.util.NoSuchElementException;
 8 import java.util.Scanner;
 9
10 import com.deitel.jhtp6.ch14.RandomAccessAccountRecord;
11
12 public class WriteRandomFile
13 {
14 private RandomAccessFile output;
15
16 private static final int NUMBER_RECORDS = 100;
17
18 // enable user to choose file to open
19 public void openFile()
20 {
21 try // open file
22 {
23 output = new RandomAccessFile( "clients.dat", "rw" );
24 } // end try
25 catch ( IOException ioException )
26 {
27 System.err.println( "File does not exist." );
28 } // end catch
29 } // end method openFile
30
31 // close file and terminate application
32 public void closeFile()
33 {
34 try // close file and exit
35 {
36 if ( output != null )
37 output.close();
38 } // end try
39 catch ( IOException ioException )
40 {
41 System.err.println( "Error closing file." );
42 System.exit( 1 );
43 } // end catch
44 } // end method closeFile
45
46 // add records to file
47 public void addRecords()
48 {
49 // object to be written to file
50 RandomAccessAccountRecord record = new RandomAccessAccountRecord();
51
52 int accountNumber = 0 ; // account number for AccountRecord object
53 String firstName; // first name for AccountRecord object
54 String lastName; // last name for AccountRecord object
55 double balance; // balance for AccountRecord object
56
57 Scanner input = new Scanner( System.in );
58
59 System.out.printf( "%s
%s
%s
%s

",
60 "To terminate input, type the end-of-file indicator ",
61 "when you are prompted to enter input.",
62 "On UNIX/Linux/Mac OS X type  d then press Enter",
63 "On Windows type  z then press Enter" );
64
65 System.out.printf( "%s %s
%s", "Enter account number (1-100),",
66 "first name, last name and balance.", "? " );
67
68 while ( input.hasNext() ) // loop until end-of-file indicator
69 {
70 try // output values to file
71 {
72 accountNumber = input.nextInt(); // read account number
73 firstName = input.next(); // read first name
74 lastName = input.next(); // read last name
75 balance = input.nextDouble(); // read balance
76
77 if ( accountNumber > 0 && accountNumber <= NUMBER_RECORDS )
78 {
79 record.setAccount( accountNumber );
80 record.setFirstName( firstName ); 
81 record.setLastName( lastName ); 
82 record.setBalance( balance ); 
83
84 output.seek( ( accountNumber - 1 ) * // position to proper
85  RandomAccessAccountRecord.SIZE ); // location for file 
86 record.write( output ); 
87 } // end if
88 else
89 System.out.println( "Account must be between 0 and 100." );
90 } // end try
91 catch ( IOException ioException )
92 {
93 System.err.println( "Error writing to file." );
94 return;
95 } // end catch
96 catch ( NoSuchElementException elementException )
97 {
98 System.err.println( "Invalid input. Please try again." );
99 input.nextLine(); // discard input so user can try again
100 } // end catch
101
102 System.out.printf( "%s %s
%s", "Enter account number (1-100),",
103 "first name, last name and balance.", "? " );
104 } // end while
105 } // end method addRecords
106 } // end class WriteRandomFile

Figure 14.27. Testing class WriteRandomFile.

(This item is displayed on page 715 in the print version)

 1 // Fig. 14.27: WriteRandomFileTest.java
 2 // This program tests class WriteRandomFile.
 3
 4 public class WriteRandomFileTest
 5 {
 6 public static void main( String args[] )
 7 {
 8 WriteRandomFile application = new WriteRandomFile();
 9 application.openFile();
10 application.addRecords();
11 application.closeFile();
12 } // end main
13 } // end class WriteRandomFileTest
 
To terminate input, type the end-of-file indicator
when you are prompted to enter input.
On UNIX/Linux/Mac OS X type  d then press Enter
On Windows type  z then press Enter

Enter account number (1-100), first name, last name and balance.
? 37 Doug Barker 0.00
Enter account number (1-100), first name, last name and balance.
? 29 Nancy Brown -24.54
Enter account number (1-100), first name, last name and balance.
? 96 Sam Stone 34.98
Enter account number (1-100), first name, last name and balance.
? 88 Dave Smith 258.34
Enter account number (1-100), first name, last name and balance.
? 33 Stacey Dunn 314.33
Enter account number (1-100), first name, last name and balance.
? ^Z
 

The user enters values for the account number, first name, last name and balance. After each record is entered, the program stores the data in RandomAccessAccountRecord object record (lines 7982 of Figure 14.26) and calls record's write method to output the data (line 86).

Lines 8485 call RandomAccessFile method seek to position the file-position pointer for object output to the byte location calculated by ( accountNumber - 1 ) * RandomAccessAccountRecord. SIZE. Account numbers in this program are in the range 1100. We subtract one from the account number when calculating the byte location of the record. Thus, for record one, the file-position pointer is set to byte zero of the file. For record 100, the file-position pointer is set to skip the first 99 records in the file.

14.7.3. Reading Data Sequentially from a Random-Access File

In the preceding sections, we created a random-access file and wrote data to it. In this section, we develop a program (Fig. 14.28Fig. 14.29) that opens a RandomAccessFile for reading with the "r" file-open mode (line 19 of Fig. 14.28), reads through the file sequentially and displays only those records containing data. The program produces an additional benefit. See whether you can determine what it iswe reveal it at the end of this section.

Figure 14.28. Reading data sequentially from a random-access file.

(This item is displayed on pages 716 - 717 in the print version)

 1 // Fig. 14.28: ReadRandomFile.java
 2 // This program reads a random-access file sequentially and
 3 // displays the contents one record at a time in text fields.
 4 import java.io.EOFException;
 5 import java.io.IOException;
 6 import java.io.RandomAccessFile;
 7
 8 import com.deitel.jhtp6.ch14.RandomAccessAccountRecord;
 9
10 public class ReadRandomFile
11 {
12 private RandomAccessFile input;
13
14 // enable user to select file to open
15 public void openFile()
16 {
17 try // open file
18 {
19 input = new RandomAccessFile( "clients.dat", "r" );
20 } // end try
21 catch ( IOException ioException )
22 {
23 System.err.println( "File does not exist." );
24 } // end catch
25 } // end method openFile
26
27 // read and display records
28 public void readRecords()
29 {
30 RandomAccessAccountRecord record = new RandomAccessAccountRecord();
31
32 System.out.printf( "%-10s%-15s%-15s%10s
", "Account",
33 "First Name", "Last Name", "Balance" );
34
35 try // read a record and display
36 {
37 while ( true )
38 {
39 do
40 {
41 record.read( input );
42 } while ( record.getAccount() == 0 );
43
44 // display record contents
45 System.out.printf( "%-10d%-12s%-12s%10.2f
",
46 record.getAccount(), record.getFirstName(),
47 record.getLastName(), record.getBalance() );
48 } // end while
49 } // end try
50 catch ( EOFException eofException ) // close file
51 {
52 return; // end of file was reached
53 } // end catch
54 catch ( IOException ioException )
55 {
56 System.err.println( "Error reading file." );
57 System.exit( 1 );
58 } // end catch
59 } // end method readRecords
60
61 // close file and terminate application
62 public void closeFile()
63 {
64 try // close file and exit
65 {
66 if ( input != null )
67 input.close();
68 } // end try
69 catch ( IOException ioException )
70 {
71 System.err.println( "Error closing file." );
72 System.exit( 1 );
73 } // end catch
74 } // end method closeFile
75 } // end class ReadRandomFile

Figure 14.29. Testing class ReadRandomFile.

(This item is displayed on page 717 in the print version)

 1 // Fig. 14.29: ReadRandomFileTest.java
 2 // Testing class ReadRandomFile.
 3
 4 public class ReadRandomFileTest
 5 {
 6 public static void main( String args[] )
 7 {
 8 ReadRandomFile application = new ReadRandomFile();
 9 application.openFile();
10 application.readRecords();
11 application.closeFile();
12 } // end main
13 } // end class ReadRandomFileTest
 
 Account First Name Last Name Balance
 29 Nancy Brown -24.54
 33 Stacey Dunn 314.33
 37 Doug Barker 0.00
 88 Dave Smith 258.34
 96 Sam Stone 34.98
 

Good Programming Practice 14.1

Open a file with the "r" file-open mode for input if the contents should not be modified. This practice prevents unintentional modification of the file's contents. This is another example of the principle of least privilege.

The program reads records by invoking method readRecords (lines 2859). This method invokes class RandomAccessAccountRecord's read method (line 41) to read one record's data into RandomAccessAccountRecord object record. Method readRecords reads from the file using two loops. The outer loop, a while statement, loops until an attempt is made to read past the end of the file. The inner loop, a do...while statement, is used to read records until one is encountered with a nonzero account number (zero is the account number for empty records). At this point, the record is displayed. When all the records have been read, the file is closed and the program terminates. Figure 14.29 contains method main and begins the execution of the program. Lines 911 open the file, call method readRecords and close the file.

What about that additional benefit we promised? If you examine the output, you will notice that the records are displayed in sorted order (by account number)! This ordering is a simple consequence of the way we stored these records in the file, using direct-access techniques. Sorting with direct-access techniques is blazingly fast. The speed is achieved by making the file large enough to hold every possible record that might be created, which enables the program to insert a record between other records without having to reorganize the file. This configuration, of course, means that the file could be sparsely occupied most of the time, a waste of storage. So this situation is another example of the space/time trade-off. By using large amounts of space, we are able to develop a much faster sorting algorithm.

14.7.4. Case Study: A Transaction-Processing Program

We now present a substantial transaction-processing program (Fig. 14.33Fig. 14.36), using a random-access file to achieve instant-access processing. The program maintains a bank's account informationit displays existing accounts, updates accounts, adds new accounts and deletes accounts. We assume that the program in Fig. 14.24Fig. 14.25 has been executed to create a file, and that the program in Fig. 14.26Fig. 14.27 has been executed to insert initial data. The techniques used in this example were presented in the earlier RandomAccessFile examples.

The program has five options. Option 1 displays a list of all the accounts in the file, using the same techniques as in the preceding section. Choosing option 1 displays the information in Fig. 14.30.

Figure 14.30. Transaction processor displaying records.

Account First Name Last Name Balance
29 Nancy Brown -24.54
33 Stacey Dunn 314.33
37 Doug Barker 0.00
88 Dave Smith 258.34
96 Sam Stone 34.98
 

Option 2 is used to update an account. The application will only update an existing record, so the function first checks to see whether the record specified by the user is empty. The record is read from the file, then the account number is compared to 0. If it is 0, the record contains no information, and a message is printed stating that the record is empty. Then, the menu choices are displayed. If the record contains information, the record's current information is displayed first. The user is then prompted for a change in the balance (either a charge or a payment), and the updated record is displayed. A typical output for option 2 is shown in Fig. 14.31.

Figure 14.31. Transaction processor updating a record.

(This item is displayed on page 719 in the print version)

Enter account to update ( 1 - 100 ): 37
37 Doug Barker 0.00

Enter charge ( + ) or payment ( - ): +87.99
37 Doug Barker 87.99
 

Option 3 is used to add a new account to the file. The user is prompted to enter information for a new record. If the user enters an account number for an existing account, an error message is displayed indicating that the record already contains information, and the menu choices are printed again. If the account number entered does not correspond to an existing record (and all the data entered is valid), the new record is created and stored in the file. This code for this option uses the same process to add a new account as does the program in Fig. 14.26Fig. 14.27. A typical output for option 3 is shown in Fig. 14.32.

Figure 14.32. Transaction processor inserting a record.

Enter account number, first name, last name and balance.
(Account number must be 1 - 100)
? 22 Sarah Johnston 247.45
 

Option 4 is used to delete a record from the file. Deletion is accomplished by asking the user for the account number and reinitializing the record (i.e., writing a blank record in its place). If the account contains no information, deleteRecord displays an error message stating that the account does not exist. Option 5 terminates program execution. The program is shown in Fig. 14.33Fig. 14.36. Figure 14.33 defines the enum type for the user's options. The options are listed in lines 711.

Figure 14.33. Transaction processor's menu options.

 1 // Fig. 14.33: MenuOption.java
 2 // Defines an enum type for the credit inquiry program's options.
 3
 4 public enum MenuOption
 5 {
 6 // declare contents of enum type
 7 PRINT( 1 ),
 8 UPDATE( 2 ),
 9 NEW( 3 ),
10 DELETE( 4 ),
11 END( 5 );
12
13 private final int value; // current menu option
14
15 MenuOption( int valueOption )
16 {
17 value = valueOption;
18 } // end MenuOptions enum constructor
19
20 public int getValue()
21 {
22 return value;
23 } // end method getValue
24 } // end enum MenuOption

Class FileEditor (Fig. 14.34) declares methods for manipulating records in a random-access file. This class uses all the techniques shown in the earlier examples. Method getrecord (lines 3145) reads the record with the given account number and stores its information in a RandomAccessAccountRecord object. Method updateRecord (lines 4864) modifies the record with the given account number, as long as the account number corresponds to a non-empty record. Method newRecord (lines 6783) adds a new record to the file using the provided account number, first name, last name and balance. Method deleteRecord (lines 86100) deletes the record with the given account number from the file, provided that the specified account exists. Method readRecords (lines 103136) displays all the currently existing records in the file.

Figure 14.34. FileEditor class that encapsulates the file-processing capabilities required in Fig. 14.35.

(This item is displayed on pages 720 - 722 in the print version)

 1 // Fig. 14.34: FileEditor.java
 2 // This class declares methods that manipulate bank account
 3 // records in a random access file.
 4 import java.io.EOFException;
 5 import java.io.File;
 6 import java.io.IOException;
 7 import java.io.RandomAccessFile;
 8 import java.util.Scanner;
 9
10 import com.deitel.jhtp6.ch14.RandomAccessAccountRecord;
11
12 public class FileEditor
13 {
14 RandomAccessFile file; // reference to the file
15 Scanner input = new Scanner( System.in );
16
17 // open the file
18 public FileEditor( String fileName ) throws IOException
19 {
20 file = new RandomAccessFile( fileName, "rw" );
21 } // end FileEditor constructor
22
23 // close the file
24 public void closeFile() throws IOException
25 {
26 if ( file != null )
27 file.close();
28 } // end method closeFile
29
30 // get a record from the file
31 public RandomAccessAccountRecord getRecord( int accountNumber )
32 throws IllegalArgumentException, NumberFormatException, IOException
33 {
34 RandomAccessAccountRecord record = new RandomAccessAccountRecord();
35
36 if ( accountNumber < 1 || accountNumber > 100 )
37 throw new IllegalArgumentException( "Out of range" );
38
39 // seek appropriate record in file
40 file.seek( ( accountNumber - 1 ) * RandomAccessAccountRecord.SIZE );
41
42 record.read( file );
43
44 return record;
45 } // end method getRecord
46
47 // update record in file
48 public void updateRecord( int accountNumber, double transaction )
49 throws IllegalArgumentException, IOException
50 {
51 RandomAccessAccountRecord record = getRecord( accountNumber );
52
53 if ( record.getAccount() == 0 )
54 throw new IllegalArgumentException( "Account does not exist" );
55
56 // seek appropriate record in file
57 file.seek( ( accountNumber - 1 ) * RandomAccessAccountRecord.SIZE );
58
59 record = new RandomAccessAccountRecord(
60 record.getAccount(), record.getFirstName(),
61 record.getLastName(), record.getBalance() + transaction );
62
63 record.write( file ); // write updated record to file
64 } // end method updateRecord
65
66 // add record to file
67 public void newRecord( int accountNumber, String firstName,
68 String lastName, double balance )
69 throws IllegalArgumentException, IOException
70 {
71 RandomAccessAccountRecord record = getRecord( accountNumber );
72
73 if ( record.getAccount() != 0 )
74 throw new IllegalArgumentException( "Account already exists" );
75
76 // seek appropriate record in file
77 file.seek( ( accountNumber - 1 ) * RandomAccessAccountRecord.SIZE );
78
79 record = new RandomAccessAccountRecord( accountNumber,
80 firstName, lastName, balance );
81
82 record.write( file ); // write record to file
83 } // end method newRecord
84
85 // delete record from file
86 public void deleteRecord( int accountNumber )
87 throws IllegalArgumentException, IOException
88 {
89 RandomAccessAccountRecord record = getRecord( accountNumber );
90
91 if ( record.getAccount() == 0 )
92 throw new IllegalArgumentException( "Account does not exist" );
93 
94 // seek appropriate record in file
95 file.seek( ( accountNumber - 1 ) * RandomAccessAccountRecord.SIZE );
96 
97 // create a blank record to write to the file
98 record = new RandomAccessAccountRecord();
99 record.write( file );
100 } // end method deleteRecord
101 
102 // read and display records
103 public void readRecords()
104 {
105 RandomAccessAccountRecord record = new RandomAccessAccountRecord();
106 
107 System.out.printf( "%-10s%-15s%-15s%10s
", "Account",
108 "First Name", "Last Name", "Balance" );
109 
110 try // read a record and display
111 {
112 file.seek( 0 );
113 
114 while ( true )
115 {
116 do
117 {
118 record.read( file );
119 } while ( record.getAccount() == 0 );
120 
121 // display record contents
122 System.out.printf( "%-10d%-15s%-15s%10.2f
",
123 record.getAccount(), record.getFirstName(),
124 record.getLastName(), record.getBalance() );
125 } // end while
126 } // end try
127 catch ( EOFException eofException ) // close file
128 {
129 return; // end of file was reached
130 } // end catch
131 catch ( IOException ioException )
132 {
133 System.err.println( "Error reading file." );
134 System.exit( 1 );
135 } // end catch
136 } // end method readRecords
137 } // end class FileEditor

Class transactionProcessor (Fig. 14.35) displays the menu for the application and manages the interactions with the FileEditor object that is created in the openFile method (lines 2034).

Figure 14.35. Transaction-processing program.

(This item is displayed on pages 723 - 726 in the print version)

 1 // Fig. 14.35: TransactionProcessor.java
 2 // A transaction processing program using random-access files.
 3 import java.io.IOException;
 4 import java.util.NoSuchElementException;
 5 import java.util.Scanner;
 6
 7 import com.deitel.jhtp6.ch14.RandomAccessAccountRecord;
 8
 9 public class TransactionProcessor
10 {
11 private FileEditor dataFile;
12 private RandomAccessAccountRecord record;
13 private MenuOption choices[] = { MenuOption.PRINT,
14 MenuOption.UPDATE, MenuOption.NEW,
15 MenuOption.DELETE, MenuOption.END };
16 
17 private Scanner input = new Scanner( System.in );
18 
19 // get the file name and open the file
20 private boolean openFile()
21 {
22 try // attempt to open file
23 {
24 // call the helper method to open the file
25 dataFile = new FileEditor( "clients.dat" );
26 } // end try
27 catch ( IOException ioException )
28 {
29 System.err.println( "Error opening file." );
30 return false;
31 } // end catch
32 
33 return true;
34 } // end method openFile
35 
36 // close file and terminate application
37 private void closeFile()
38 {
39 try // close file
40 {
41 dataFile.closeFile();
42 } // end try
43 catch ( IOException ioException )
44 {
45 System.err.println( "Error closing file." );
46 System.exit( 1 );
47 } // end catch
48 } // end method closeFile
49 
50 // create, update or delete the record
51 private void performAction( MenuOption action )
52 {
53 int accountNumber = 0; // account number of record
54 String firstName; // first name for account
55 String lastName; // last name for account
56 double balance; // account balance
57 double transaction; // amount to change in balance
58 
59 try // attempt to manipulate files based on option selected
60 {
61 switch ( action ) // switch based on option selected
62 {
63 case PRINT:
64 System.out.println();
65 dataFile.readRecords();
66 break;
67 case NEW:
68 System.out.printf( "
%s%s
%s
%s",
69 "Enter account number,",
70 " first name, last name and balance.",
71 "(Account number must be 1 - 100)", "? " );
72 
73 accountNumber = input.nextInt(); // read account number
74 firstName = input.next(); // read first name
75 lastName = input.next(); // read last name
76 balance = input.nextDouble(); // read balance
77 
78 dataFile.newRecord( accountNumber, firstName, 
79  lastName, balance ); // create new record 
80 break;
81 case UPDATE:
82 System.out.print(
83 "
Enter account to update ( 1 - 100 ): " );
84 accountNumber = input.nextInt();
85 record = dataFile.getRecord( accountNumber );
86 
87 if ( record.getAccount() == 0 )
88 System.out.println( "Account does not exist." );
89 else
90 {
91 // display record contents
92 System.out.printf( "%-10d%-12s%-12s%10.2f

",
93 record.getAccount(), record.getFirstName(),
94 record.getLastName(), record.getBalance() );
95 
96 System.out.print(
97 "Enter charge ( + ) or payment ( - ): " );
98 transaction = input.nextDouble();
99 dataFile.updateRecord( accountNumber, // update record
100  transaction ); 
101 
102 // retrieve updated record
103 record = dataFile.getRecord( accountNumber );
104 
105 // display updated record
106 System.out.printf( "%-10d%-12s%-12s%10.2f
",
107 record.getAccount(), record.getFirstName(),
108 record.getLastName(), record.getBalance() );
109 } // end else
110 break;
111 case DELETE:
112 System.out.print(
113 "
Enter an account to delete (1 - 100): " );
114 accountNumber = input.nextInt();
115 
116 dataFile.deleteRecord( accountNumber ); // delete record
117 break;
118 default:
119 System.out.println( "Invalid action." );
120 break;
121 } // end switch
122 } // end try
123 catch ( NumberFormatException format )
124 {
125 System.err.println( "Bad input." );
126 } // end catch
127 catch ( IllegalArgumentException badAccount )
128 {
129 System.err.println( badAccount.getMessage() );
130 } // end catch
131 catch ( IOException ioException )
132 {
133 System.err.println( "Error writing to the file." );
134 } // end catch
135 catch ( NoSuchElementException elementException )
136 {
137 System.err.println( "Invalid input. Please try again." );
138 input.nextLine(); // discard input so user can try again
139 } // end catch
140 } // end method performAction
141 
142 // enable user to input menu choice
143 private MenuOption enterChoice()
144 {
145 int menuChoice = 1;
146 
147 // display available options
148 System.out.printf( "
%s
%s
%s
%s
%s
%s",
149 "Enter your choice", "1 - List accounts",
150 "2 - Update an account", "3 - Add a new account",
151 "4 - Delete an account", "5 - End program
? " );
152 
153 try
154 {
155 menuChoice = input.nextInt();
156 }
157 catch ( NoSuchElementException elementException )
158 {
159 System.err.println( "Invalid input." );
160 System.exit( 1 );
161 } // end catch
162 
163 return choices[ menuChoice - 1 ]; // return choice from user
164 } // end enterChoice
165 
166 public void processRequests()
167 {
168 openFile();
169 
170 // get user's request
171 MenuOption choice = enterChoice();
172 
173 while ( choice != MenuOption.END )
174 {
175 performAction (choice );
176 choice = enterChoice();
177 } // end while
178 
179 closeFile();
180 } // end method processRequests
181 } // end class TransactionProcessor

Figure 14.36. Testing class transactionProcessor.

(This item is displayed on page 726 in the print version)

 1 // Fig. 14.36: TransactionProcessorTest.java
 2 // Testing the transaction processor.
 3
 4 public class TransactionProcessorTest
 5 {
 6 public static void main( String args[] )
 7 {
 8 TransactionProcessor application = new TransactionProcessor();
 9 application.processRequests();
10 } // end main
11 } // end class TransactionProcessorTest

Method processRequests (lines 166180) processes the choices entered by the user. If the user does not enter 5 (which ends the program), method performAction (lines 51140) is called. This method inputs information from the user and sends it to the appropriate method of class FileEditor (Fig. 14.34), which encapsulates the file-processing operations in this example. The method to call is determined by performAction's MenuOption argument. Each option is handled in the switch statement of lines 61121. Method performAction also handles any exceptions that might be thrown from FileEditor's methods.

Introduction to Computers, the Internet and the World Wide Web

Introduction to Java Applications

Introduction to Classes and Objects

Control Statements: Part I

Control Statements: Part 2

Methods: A Deeper Look

Arrays

Classes and Objects: A Deeper Look

Object-Oriented Programming: Inheritance

Object-Oriented Programming: Polymorphism

GUI Components: Part 1

Graphics and Java 2D™

Exception Handling

Files and Streams

Recursion

Searching and Sorting

Data Structures

Generics

Collections

Introduction to Java Applets

Multimedia: Applets and Applications

GUI Components: Part 2

Multithreading

Networking

Accessing Databases with JDBC

Servlets

JavaServer Pages (JSP)

Formatted Output

Strings, Characters and Regular Expressions

Appendix A. Operator Precedence Chart

Appendix B. ASCII Character Set

Appendix C. Keywords and Reserved Words

Appendix D. Primitive Types

Appendix E. (On CD) Number Systems

Appendix F. (On CD) Unicode®

Appendix G. Using the Java API Documentation

Appendix H. (On CD) Creating Documentation with javadoc

Appendix I. (On CD) Bit Manipulation

Appendix J. (On CD) ATM Case Study Code

Appendix K. (On CD) Labeled break and continue Statements

Appendix L. (On CD) UML 2: Additional Diagram Types

Appendix M. (On CD) Design Patterns

Appendix N. Using the Debugger

Inside Back Cover



Java(c) How to Program
Java How to Program (6th Edition) (How to Program (Deitel))
ISBN: 0131483986
EAN: 2147483647
Year: 2003
Pages: 615

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net