AhmadLang / Java, How To Program, 2004
.pdf
34// ensure that name is proper length
35private String readName( RandomAccessFile file ) throws IOException
36{
37char name[] = new char[ 15 ], temp;
38
39 for ( int count = 0; count < name.length; count++ )
40{
41temp = file.readChar();
42name[ count ] = temp;
43} // end for
44
45return new String( name ).replace( '\0', ' ' );
46} // end method readName
47
48// write a record to specified RandomAccessFile
49public void write( RandomAccessFile file ) throws IOException
50{
51file.writeInt( getAccount() );
52writeName( file, getFirstName() );
53writeName( file, getLastName() );
54file.writeDouble( getBalance() );
55} // end method write
56
57// write a name to file; maximum of 15 characters
58private void writeName( RandomAccessFile file, String name )
59throws IOException
60{
61StringBuffer buffer = null;
62
63if ( name != null )
64buffer = new StringBuffer( name );
65else
66buffer = new StringBuffer( 15 );
68buffer.setLength( 15 );
69file.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.
[Page 710]
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 '\0'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
(This item is displayed on page 712 in the print version)
1// Fig. 14.25: CreateRandomFileTest.java
2// Testing class CreateRandomFile.
3
4public class CreateRandomFileTest
5{
6public static void main( String args[] )
7{
8CreateRandomFile application = new CreateRandomFile();
9application.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.
[Page 712]
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.
[Page 714]
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. |
|
4import java.io.File;
5import java.io.IOException;
6import java.io.RandomAccessFile;
7import java.util.NoSuchElementException;
8import java.util.Scanner;
9
10 import com.deitel.jhtp6.ch14.RandomAccessAccountRecord; 11
12 public class WriteRandomFile
13{
14private RandomAccessFile output;
16private static final int NUMBER_RECORDS = 100;
18// enable user to choose file to open
19public void openFile()
20{
21try // open file
22{
23output = new RandomAccessFile( "clients.dat", "rw" );
24} // end try
25catch ( IOException ioException )
26{
27System.err.println( "File does not exist." );
28} // end catch
29} // end method openFile
30
31// close file and terminate application
32public void closeFile()
33{
34try // close file and exit
35{
36if ( output != null )
37 |
output.close(); |
38} // end try
39catch ( IOException ioException )
40{
41System.err.println( "Error closing file." );
42System.exit( 1 );
43} // end catch
44} // end method closeFile
45
46// add records to file
47public void addRecords()
48{
49// object to be written to file
50RandomAccessAccountRecord record = new RandomAccessAccountRecord();
52int accountNumber = 0 ; // account number for AccountRecord object
53String firstName; // first name for AccountRecord object
54String lastName; // last name for AccountRecord object
55double balance; // balance for AccountRecord object
57Scanner input = new Scanner( System.in );
59System.out.printf( "%s\n%s\n%s\n%s\n\n",
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 <ctrl> d then press Enter",
63"On Windows type <ctrl> z then press Enter" );
64
65System.out.printf( "%s %s\n%s", "Enter account number (1-100),",
66"first name, last name and balance.", "? " );
67
68while ( input.hasNext() ) // loop until end-of-file indicator
69{
70try // 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
91catch ( IOException ioException )
92{
93 |
System.err.println( "Error writing to file." ); |
94 |
return; |
95} // end catch
96catch ( 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\n%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.
4public class WriteRandomFileTest
5{
6public static void main( String args[] )
7{
8WriteRandomFile application = new WriteRandomFile();
9application.openFile();
10application.addRecords();
11application.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 <ctrl> d then press Enter On Windows type <ctrl> 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.
[Page 715]
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. |
|
4import java.io.EOFException;
5import java.io.IOException;
6import java.io.RandomAccessFile;
8import com.deitel.jhtp6.ch14.RandomAccessAccountRecord;
10public class ReadRandomFile
11{
12private RandomAccessFile input;
14// enable user to select file to open
15public void openFile()
16{
17try // open file
18{
19input = new RandomAccessFile( "clients.dat", "r" );
20} // end try
21catch ( IOException ioException )
22{
23System.err.println( "File does not exist." );
24} // end catch
25} // end method openFile
26
27// read and display records
28public void readRecords()
29{
30RandomAccessAccountRecord record = new RandomAccessAccountRecord();
32System.out.printf( "%-10s%-15s%-15s%10s\n", "Account",
33 |
"First Name", "Last Name", "Balance" ); |
34 |
|
35try // read a record and display
36{
37while ( 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\n", |
||
46 |
|
record.getAccount(), record.getFirstName(), |
||
47 |
|
record.getLastName(), record.getBalance() ); |
||
48} // end while
49} // end try
50catch ( EOFException eofException ) // close file
51{
52return; // end of file was reached
53} // end catch
54catch ( IOException ioException )
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.
[Page 719]
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 |
|
|
|
4public enum MenuOption
5{
6 // declare contents of enum type
7PRINT( 1 ),
8UPDATE( 2 ),
9NEW( 3 ),
10DELETE( 4 ),
11END( 5 );
12
13 private final int value; // current menu option
14
15MenuOption( int valueOption )
16{
17value = valueOption;
18} // end MenuOptions enum constructor
20public int getValue()
21{
22return value;
23} // end method getValue
24} // end enum MenuOption
[Page 720]
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. |
|
4import java.io.EOFException;
5import java.io.File;
6import java.io.IOException;
7import java.io.RandomAccessFile;
8import java.util.Scanner;
9
10 import com.deitel.jhtp6.ch14.RandomAccessAccountRecord; 11
12public class FileEditor
13{
14RandomAccessFile file; // reference to the file
15Scanner input = new Scanner( System.in );
16
17// open the file
18public FileEditor( String fileName ) throws IOException
19{
20file = new RandomAccessFile( fileName, "rw" );
21} // end FileEditor constructor
22
23// close the file
24public void closeFile() throws IOException
25{
26if ( file != null )
27file.close();
28} // end method closeFile
30// get a record from the file
31public RandomAccessAccountRecord getRecord( int accountNumber )
32throws IllegalArgumentException, NumberFormatException, IOException
33{
34RandomAccessAccountRecord record = new RandomAccessAccountRecord();
36if ( accountNumber < 1 || accountNumber > 100 )
37 |
throw new IllegalArgumentException( "Out of range" ); |
38 |
|
39// seek appropriate record in file
40file.seek( ( accountNumber - 1 ) * RandomAccessAccountRecord.SIZE );
42record.read( file );
44return record;
45} // end method getRecord
47// update record in file
48public void updateRecord( int accountNumber, double transaction )
49throws IllegalArgumentException, IOException
50{
51RandomAccessAccountRecord record = getRecord( accountNumber );
53if ( record.getAccount() == 0 )
54 |
throw new IllegalArgumentException( "Account does not exist" ); |
55 |
|
56// seek appropriate record in file
57file.seek( ( accountNumber - 1 ) * RandomAccessAccountRecord.SIZE );
