Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

AhmadLang / Java, How To Program, 2004

.pdf
Скачиваний:
630
Добавлен:
31.05.2015
Размер:
51.82 Mб
Скачать

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

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 ('\0') 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.

3import java.io.IOException;

4import java.io.RandomAccessFile;

6import com.deitel.jhtp6.ch14.RandomAccessAccountRecord;

8public class CreateRandomFile

9{

10 private static final int NUMBER_RECORDS = 100; 11

12// enable user to select file to open

13public void createFile()

14{

15RandomAccessFile file = null;

16

17try // open file for reading and writing

18{

19file = new RandomAccessFile( "clients.dat", "rw" );

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

29System.out.println( "Created file clients.dat." );

31System.exit( 0 ); // terminate program

32} // end try

33catch ( IOException ioException )

34{

35System.err.println( "Error processing file." );

36System.exit( 1 );

37} // end catch

38finally

39{

40try

41{

42

if ( file != null )

 

43

file.close(); //

close file

44} // end try

45catch ( 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

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 )

55{

56System.err.println( "Error reading file." );

57System.exit( 1 );

58} // end catch

59} // end method readRecords

60

61// close file and terminate application

62public void closeFile()

63{

64try // close file and exit

65{

66if ( input != null )

67

input.close();

68} // end try

69catch ( IOException ioException )

70{

71System.err.println( "Error closing file." );

72System.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

4public class ReadRandomFileTest

5{

6public static void main( String args[] )

7{

8ReadRandomFile application = new ReadRandomFile();

9application.openFile();

10application.readRecords();

11application.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.

[Page 717]

[Page 718]

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.

[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 );