Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Java How to Program, Fourth Edition - Deitel H., Deitel P.pdf
Скачиваний:
58
Добавлен:
24.05.2014
Размер:
14.17 Mб
Скачать

16

Files and Streams

Objectives

To be able to create, read, write and update files.

To understand the Java streams class hierarchy.

To be able to use the FileInputStream and

FileOutputStream classes.

To be able to use the ObjectInputStream and

ObjectOutputStream classes.

To be able to use class RandomAccessFile.

To be able to use a JFileChooser dialog to access files and directories.

To become familiar with sequential-access and random-access file processing.

To be able to use class File.

I can only assume that a “Do Not File” document is filed in a “Do Not File” file.

Senator Frank Church

Senate Intelligence Subcommittee Hearing, 1975

Consciousness … does not appear to itself chopped up in bits. … A “river” or a “stream” are the metaphors by which it is most naturally described.

William James

I read part of it all the way through.

Samuel Goldwyn

It is quite a three-pipe problem.

Sir Arthur Conan Doyle

Chapter 16

Files and Streams

895

Outline

16.1Introduction

16.2Data Hierarchy

16.3Files and Streams

16.4Creating a Sequential-Access File

16.5Reading Data from a Sequential-Access File

16.6Updating Sequential-Access Files

16.7Random-Access Files

16.8Creating a Random-Access File

16.9Writing Data Randomly to a Random-Access File

16.10Reading Data Sequentially from a Random-Access File

16.11Example: A Transaction-Processing Program

16.12Class File

Summary • Terminology • Self-Review Exercises • Answers to Self-Review Exercises • Exercises

16.1 Introduction

Storage of data in variables and arrays is temporary—the data is lost when a local variable “goes out of scope” or when the program terminates. Programs use files for long-term retention of large amounts of data, even after programs that create the data terminate. We refer to data maintained in files as persistent data, because the data exists beyond the duration of program execution. Computers store files on secondary storage devices such as magnetic disks, optical disks and magnetic tapes. In this chapter, we explain how Java programs create, update and process data files. We consider both “sequential-access” files and “ran- dom-access” files and discuss typical applications for each.

File processing is one of the most important capabilities a language must have to support commercial applications that typically process massive amounts of persistent data. In this chapter, we discuss Java’s powerful file-processing and stream input/output features. File processing is a subset of Java’s stream-processing capabilities that enable a program to read and write bytes in memory, in files and over network connections. We have two goals in this chapter—to introduce file-processing paradigms and to provide the reader with sufficient stream-processing capabilities to support the networking features introduced in Chapter 17.

Software Engineering Observation 16.1

It would be dangerous to enable applets arriving from anywhere on the World Wide Web to be able to read and write files on the client system. By default, Web browsers prevent applets from performing file processing on the client system. Therefore, file-processing programs generally are implemented as Java applications.

16.2 Data Hierarchy

Ultimately, a computer processes all data items as combinations of zeros and ones, because it is simple and economical for engineers to build electronic devices that can assume two stable states—one state represents 0, the other state represents 1. It is remarkable that the

896 Files and Streams

Chapter 16

impressive functions performed by computers involve only the most fundamental manipulations of 0s and 1s.

The smallest data item in a computer can assume the value 0 or the value 1. Such a data item is called a bit (short for “binary digit”—a digit that can assume one of two values). Computer circuitry performs various simple bit manipulations, such as examining the value of a bit, setting the value of a bit and reversing a bit (from 1 to 0 or from 0 to 1).

It is cumbersome for programmers to work with data in the low-level form of bits. Instead, programmers prefer to work with data in such forms as decimal digits (0–9), letters (A–Z and a–z), and special symbols (e.g., $, @, %, &, *, (, ), -, +, ", :, ?, / and many others). Digits, letters and special symbols are known as characters. The computer’s character set is the set of all characters used to write programs and represent data items. Computers can process only 1s and 0s, so a computer’s character set represents every character as a pattern of 1s and 0s. Characters in Java are Unicode characters composed of 2 bytes. Bytes are most commonly composed of eight bits. Programmers create programs and data items with characters. Computers manipulate and process these characters as patterns of bits. See Appendix K for more information on Unicode.

Just as characters are composed of bits, fields are composed of characters or bytes. A field is a group of characters or bytes that conveys meaning. For example, a field consisting of uppercase and lowercase letters can be used to represent a person’s name.

Data items processed by computers form a data hierarchy in which data items become larger and more complex in structure as we progress from bits, to characters, to fields, etc.

Typically, several fields (called instance variables in Java) compose a record (implemented as a class in Java). In a payroll system, for example, a record for a particular employee might consist of the following fields (possible data types for these fields are shown in parentheses following each field):

Employee identification number (int)

Name (String)

Address (String)

Hourly pay rate (double)

Number of exemptions claimed (int)

Year-to-date earnings (int or double)

Amount of taxes withheld (int or double)

Thus, a record is a group of related fields. In the preceding example, each of the fields belongs to the same employee. Of course, a particular company might have many employees and will have a payroll record for each employee. A file is a group of related records.1 A company’s payroll file normally contains one record for each employee. Thus, a payroll file for a small company might contain only 22 records, whereas a payroll file for a large company might contain 100,000 records. It is not unusual for a company to have many files, some containing millions, or even billions, of characters of information. Figure 16.1 illustrates the data hierarchy.

1.More generally, a file can contain arbitrary data in arbitrary formats. In some operating systems, a file is viewed as nothing more than a collection of bytes. In such an operating system, any organization of the bytes in a file (such as organizing the data into records) is a view created by the applications programmer.

Chapter 16

 

 

Files and Streams

897

 

 

 

 

 

 

 

 

 

 

 

 

Sally

Black

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Tom

Blue

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Judy

Green

 

 

File

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Iris

Orange

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Randy

Red

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Judy

Green

 

 

 

Record

 

 

 

 

 

J u

 

d y

Field

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

01001010

Byte (ASCII character J)

 

1Bit

Fig. 16.1 The data hierarchy.

To facilitate the retrieval of specific records from a file, at least one field in each record is chosen as a record key. A record key identifies a record as belonging to a particular person or entity that is unique from all other records. In the payroll record described previously, the employee identification number normally would be chosen as the record key.

There are many ways to organize records in a file. The most common organization is called a sequential file in which records are stored in order by the record-key field. In a payroll file, records are placed in order by employee identification number. The first employee record in the file contains the lowest employee identification number, and subsequent records contain increasingly higher employee identification numbers.

Most businesses store data in many different files. For example, companies might have payroll files, accounts receivable files (listing money due from clients), accounts payable files (listing money due to suppliers), inventory files (listing facts about all the items handled by the business) and many other file types. Often, a group of related files is called a database. A collection of programs designed to create and manage databases is called a database management system (DBMS).

16.3 Files and Streams

Java views each file as a sequential stream of bytes (Fig. 16.2). Each file ends either with an end-of-file marker or at a specific byte number recorded in a system-maintained administrative data structure. Java abstracts this concept from the programmer. A Java program processing a stream of bytes simply receives an indication from the system when the program reaches the end of the stream—the program does not need to know how the underly-

898 Files and Streams

Chapter 16

ing platform represents files or streams. In some cases, the end-of-file indication occurs as an exception. In other cases, the indication is a return value from a method invoked on a stream-processing object. We demonstrate both cases in this chapter.

A Java program opens a file by creating an object and associating a stream of bytes with the object. Java also can associates streams of bytes associated with devices. In fact, Java creates three stream objects that are associated with devices when a Java program begins executing—System.in, System.out and System.err. The streams associated with these objects provide communication channels between a program and a particular device. For example, object System.in (the standard input stream object) normally enables a program to input bytes from the keyboard, object System.out (the standard output stream object) normally enables a program to output data to the screen and object System.err (the standard error stream object) normally enables a program to output error messages to the screen. Each of these streams can be redirected. For System.in, this enables the program to read bytes from a different source. For System.out and System.err, this enables the output to be sent to a different location, such as a file on disk. Class System provides methods setIn, setOut and setErr to redirect the standard input, output and error streams.

Java programs perform file processing by using classes from package java.io. This package includes definitions for the stream classes, such as FileInputStream (for byte-based input from a file), FileOutputStream (for byte-based output to a file), FileReader (for character-based input from a file) and FileWriter (for characterbased output to a file). Files are opened by creating objects of these stream classes that inherit from classes InputStream, OutputStream, Reader and Writer, respectively. Thus, the methods of these stream classes can all be applied to file streams as well. To perform input and output of data types, objects of class ObjectInputStream,

DataInputStream, ObjectOutputStream and DataOutputStream will be used together with the byte-based file stream classes FileInputStream and FileOutputStream. Figure 16.3 summarizes the inheritance relationships of many of the Java I/O classes (abstract classes are shown in italic font). The following discussion overviews the capabilities of each of the classes in Fig. 16.3.

Java offers many classes for performing input/output. This section briefly overviews many of these classes and explains how they relate to one another. In the rest of the chapter, we use several of these stream classes as we implement a variety of file-processing programs that create, manipulate and destroy sequential-access files and random-access files. We also include a detailed example on class File, which is useful for obtaining information about files and directories. In Chapter 17, Networking, we use stream classes extensively to implement networking applications.

 

0 1 2 3 4 5 6 7 8 9

...

n-1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

...

 

end-of-file marker

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Fig. 16.2 Java’s view of a file of n bytes.

Chapter 16

Files and Streams

899

A portion of the class hierarchy of the java.io package

java.lang.Object File FileDescriptor

InputStream

ByteArrayInputStream

FileInputStream

FilterInputStream

BufferedInputStream

DataInputStream

PushbackInputStream

ObjectInputStream

PipedInputStream

SequenceInputStream

OutputStream

ByteArrayOutputStream

FileOutputStream

FilterOutputStream

BufferedOutputStream

DataOutputStream

PrintStream

ObjectOutputStream

PipedOutputStream

RandomAccessFile

Reader

BufferedReader

LineNumberReader

CharArrayReader

FilterReader

PushbackReader

InputStreamReader

FileReader

PipedReader

StringReader

Fig. 16.3 A portion of the class hierarchy of the java.io package (part 1 of 2).

900 Files and Streams

Chapter 16

A portion of the class hierarchy of the java.io package

Writer

BufferedWriter

CharArrayWriter

FilterWriter

OutputStreamWriter

FileWriter

PipedWriter

PrintWriter

StringWriter

Fig. 16.3 A portion of the class hierarchy of the java.io package (part 2 of 2).

InputStream and OutputStream (subclasses of Object) are abstract classes that define methods for performing byte-based input and output, respectively.

Programs perform byte-based file input/output with FileInputStream (a subclass of InputStream) and FileOutputStream (a subclass of OutputStream). We use these classes extensively in the examples in this chapter.

Pipes are synchronized communication channels between threads or processes. Java provides PipedOutputStream (a subclass of OutputStream) and PipedInputStream (a subclass of InputStream) to establish pipes between two threads. One thread sends data to another by writing to a PipedOutputStream. The target thread reads information from the pipe via a PipedInputStream.

A PrintStream (a subclass of FilterOutputStream) performs text output to the specified stream. Actually, we have been using PrintStream output throughout the text to this point—System.out is a PrintStream, as is System.err.

A FilterInputStream filters an InputStream, and a FilterOutStream filters an OutputStream; filtering simply means that the filter stream provides additional functionality, such as buffering, monitoring line numbers or aggregating data bytes into meaningful primitive-data-type units. FilterInputStream and FilterOutputStream are abstract classes, so additional functionality is provided by their subclasses.

Reading data as raw bytes is fast but crude. Usually programs read data as aggregates of bytes that form an int, a float, a double and so on. Java programs can use several classes to input and output data in aggregate form.

A RandomAccessFile is useful for direct-access applications, such as transactionprocessing applications like airline-reservations systems and point-of-sale systems. 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 file—perhaps one widely separated from the part of the file referenced in the previous request. Direct-access applications provide rapid access to specific data items in large files; often, such applications are used in applications that require users to wait for answers—these answers must be made available quickly, or the people might become impatient and “take their business elsewhere.”

Chapter 16 Files and Streams 901

The DataInput interface is implemented by class DataInputStream and class RandomAccessFile (discussed later in the chapter); each needs to read primitive data types from a stream. DataInputStreams enable a program to read binary data from an InputStream. The DataInput interface includes methods read (for byte arrays), readBoolean, readByte, readChar, readDouble, readFloat, readFully (for byte arrays), readInt, readLong, readShort, readUnsignedByte, readUnsignedShort, readUTF (for strings) and skipBytes.

The DataOutput interface is implemented by class DataOutputStream (a subclass of FilterOutputStream) and class RandomAccessFile; each needs to write primitive data types to an OutputStream. DataOutputStreams enable a program to write binary data to an OutputStream. The DataOutput interface includes methods flush, size, write (for a byte), write (for a byte array), writeBoolean, writeByte, writeBytes, writeChar, writeChars (for Unicode Strings), writeDouble, writeFloat, writeInt, writeLong, writeShort and writeUTF.

Buffering is an I/O-performance-enhancement technique. With a BufferedOutputStream (a subclass of class FilterOutputStream), each output statement does not necessarily result in an actual physical transfer of data to the output device. Rather, each output operation is directed to a region in memory called a buffer that is large enough to hold the data of many output operations. Then, actual transfer to the output device is performed in one large physical output operation each time the buffer fills. The output operations directed to the output buffer in memory are often called logical output operations. With a BufferedOutputStream, a partially filled buffer can be forced out to the device at any time by invoking the stream object’s flush method.

Performance Tip 16.1

Since typical physical output operations are extremely slow compared to the speed of access- ing computer memory, buffered outputs normally yield significant performance improvements over unbuffered outputs.

With a BufferedInputStream (a subclass of class FilterInputStream), many “logical” chunks of data from a file are read as one large physical input operation into a memory buffer. As a program requests each new chunk of data, it is taken from the buffer (this is sometimes referred to as a logical input operation). When the buffer is empty, the next actual physical input operation from the input device is performed to read in the next group of “logical” chunks of data. Thus, the number of actual physical input operations is small compared with the number of read requests issued by the program.

Performance Tip 16.2

Since typical input operations are extremely slow compared to the speed of accessing com- puter memory, buffered inputs normally yield significant performance improvements over unbuffered inputs.

A PushbackInputStream (a subclass of class FilterInputStream) is for applications more exotic than those most programmers require. Essentially, the application reading a PushbackInputStream reads bytes from the stream and forms aggregates consisting of several bytes. Sometimes, to determine that one aggregate is complete, the application must read the first character “past the end” of the first aggregate. Once the program determines that the current aggregate is complete, the extra character is “pushed

902 Files and Streams

Chapter 16

back” onto the stream. PushbackInputStreams are used by programs (like compilers) that parse their inputs—that is, break them into meaningful units (such as the keywords, identifiers and operators that the compiler must recognize).

When object instance variables are output to a disk file, in a sense we lose the object’s type information. We have only data, not type information, on a disk. If the program that is going to read this data knows what object type it corresponds to, then the data is simply read into objects of that type. Sometimes, we would like to read or write an entire object to a file. The ObjectInputStream and ObjectOutputStream classes, which respectively implement the ObjectInput and ObjectOutput interfaces, enable an entire object to be read from or written to a file (or other stream type). We often chain ObjectInputStreams to FileInputStreams. (We also chain ObjectOutputStreams to FileOutputStreams.) The ObjectOutput interface contains method writeObject, which takes an Object that implements interface Serializable as an argument and writes its information to the OutputStream. Correspondingly, the ObjectInput interface requires method readObject, which reads and returns an Object from an InputStream. After the reading of an object, it can be cast to the desired type. Additionally, these interfaces include other Object-centric methods as well as the same methods as DataInput and DataOutput for reading and writing primitive data types.

Java stream I/O includes capabilities for inputting from byte arrays in memory and outputting to byte arrays in memory. A ByteArrayInputStream (a subclass of

InputStream) reads from a byte array in memory. A ByteArrayOutputStream

(a subclass of OutputStream) outputs to a byte array in memory. One application of byte-array I/O is data validation. A program can input an entire line at a time from the input stream into a byte array. Then, a validation routine can scrutinize the contents of the byte array and correct the data, if necessary. Then, the program can proceed to input from the byte array, knowing that the input data is in the proper format. Outputting to a byte array is a nice way to take advantage of the powerful output-formatting capabilities of Java streams. For example, data can be prepared in a byte array, using the same formatting that will be displayed at a later time, then output to a disk file to preserve the screen image.

A SequenceInputStream (a subclass of InputStream) enables concatenation of several InputStreams, so that the program sees the group as one continuous InputStream. As the program reaches the end of an input stream, that stream closes and the next stream in the sequence opens.

In addition to the byte based streams, Java provides Reader and Writer classes, which are Unicode, two-byte, character based streams. Most of the byte-based streams have corresponding character-based Reader or Writer classes.

Class BufferedReader (a subclass of abstract class Reader) and class BufferedWriter (a subclass of abstract class Writer) enable efficient buffering for character-based streams. Character-based streams use Unicode characters—such streams can process data in any language that the Unicode character set represents.

Class CharArrayReader and class CharArrayWriter read and write a stream of characters to a character array.

A PushbackReader (a subclass of abstract class FilterReader) enables characters to be pushed back on a character stream. A LineNumberReader (a subclass of BufferedReader) is a buffered character-stream that keeps track of line numbers (i.e., a newline, a return or a carriage-return line-feed combination).

Chapter 16

Files and Streams

903

Class FileReader (a subclass of InputStreamReader) and class FileWriter (a subclass of OutputStreamWriter) read characters from and write characters to a file, respectively. Class PipedReader and class PipedWriter implement piped-character streams that can be used to transfer information between threads. Class StringReader and StringWriter read and write characters to Strings. A PrintWriter writes characters to a stream.

Class File enables programs to obtain information about a file or directory. We discuss class File extensively in Section 16.12.

16.4 Creating a Sequential-Access File

Java imposes no structure on a file. Notions like “record” do not exist in Java files. Therefore, the programmer must structure files to meet the requirements of applications. In the following example, we see how the programmer can impose a simple record structure on a file. First we present the program, then we analyze it in detail.

The program of Fig. 16.4–Fig. 16.6 creates a simple sequential-access file that might be used in an accounts receivable system to help manage the money owed by a company’s credit clients. For each client, the program obtains an account number, the client’s first name, the client’s last name and the client’s balance (i.e., the amount the client still owes the company for goods and services received in the past). The data obtained for each client constitutes a record for that client. The program uses the account number as the record key; that is, the file will be created and maintained in account-number order. [Note: This program assumes the user enters the records in account-number order. In a comprehensive accounts receivable system, a sorting capability would be provided so the user could enter the records in any order—the records would then be sorted and written to the file.]

Most of the programs in this chapter have a similar GUI, so this program defines class BankUI (Fig. 16.4) to encapsulate the GUI. (See the second sample output screen in Fig. 16.6.) Also, the program defines class AccountRecord (Fig. 16.5) to encapsulate the client record information (i.e., account, first name, etc.) used by the examples in this chapter. For reuse, classes BankUI and AccountRecord are defined in package com.deitel.jhtp4.ch16.

[Note: Most of the programs in this chapter use classes BankUI and AccountRecord. When you compile these classes, or any others that will be reused in this chapter, you should place the classes in a common directory. When you compile classes that use BankUI and AccountRecord, be sure to specify the -classpath command line argument to both javac and java, as in

javac -classpath .;packageLocation ClassName.java java -classpath .;packageLocation ClassName

where packageLocation represents the common directory in which the classes of the package com.deitel.jhtp4.ch16 reside and ClassName represents the class to compile or execute. Be sure to include the current directory (specified with .) in the class path. [Note: If your packaged classes are in a JAR file, the packageLocation should include the location and name of the actual JAR file.] Also, the path separator shown (;, which is used in Microsoft Windows) should be appropriate for your platform (such as : on UNIX/Linux).]

Class BankUI (Fig. 16.4) contains two JButtons and arrays of JLabels and JTextFields. The number of JLabels and JTextFields is set with the constructor

904 Files and Streams

Chapter 16

defined at lines 34–75. Methods getFieldValues (lines 116–124), setFieldValues (lines 104–113) and clearFields (lines 96–100) manipulate the text of the

JTextFields. Methods getFields (lines 90–93), getDoTask1Button (lines 78– 81) and getDoTask2Button (lines 84–87) return individual GUI components, so that a client program can add ActionListeners (for example).

1// Fig. 16.4: BankUI.java

2 // A reusable GUI for the examples in this chapter. 3 package com.deitel.jhtp4.ch16;

4

5 // Java core packages

6 import java.awt.*;

7

8 // Java extension packages

9 import javax.swing.*;

10

11 public class BankUI extends JPanel {

12

13// label text for GUI

14protected final static String names[] = { "Account number",

15"First name", "Last name", "Balance",

16"Transaction Amount" };

17

18// GUI components; protected for future subclass access

19protected JLabel labels[];

20protected JTextField fields[];

21protected JButton doTask1, doTask2;

22protected JPanel innerPanelCenter, innerPanelSouth;

23

24// number of text fields in GUI

25protected int size;

26

27// constants representing text fields in GUI

28public static final int ACCOUNT = 0, FIRSTNAME = 1,

29LASTNAME = 2, BALANCE = 3, TRANSACTION = 4;

30

31// Set up GUI. Constructor argument of 4 creates four rows

32// of GUI components. Constructor argument of 5 (used in a

33// later program) creates five rows of GUI components.

34public BankUI( int mySize )

35{

36size = mySize;

37labels = new JLabel[ size ];

38fields = new JTextField[ size ];

39

40// create labels

41for ( int count = 0; count < labels.length; count++ )

42

labels[ count ] = new JLabel( names[ count ] );

43

 

44// create text fields

45for ( int count = 0; count < fields.length; count++ )

46

fields[ count ] = new JTextField();

Fig. 16.4 BankUI contains a reusable GUI for several programs (part 1 of 3).

Chapter 16

Files and Streams

905

47

48// create panel to lay out labels and fields

49innerPanelCenter = new JPanel();

50innerPanelCenter.setLayout( new GridLayout( size, 2 ) );

52// attach labels and fields to innerPanelCenter

53for ( int count = 0; count < size; count++ ) {

54

innerPanelCenter.add( labels[ count ] );

55innerPanelCenter.add( fields[ count ] );

56}

57

58// create generic buttons; no labels or event handlers

59doTask1 = new JButton();

60doTask2 = new JButton();

61

62// create panel to lay out buttons and attach buttons

63innerPanelSouth = new JPanel();

64innerPanelSouth.add( doTask1 );

65innerPanelSouth.add( doTask2 );

66

67// set layout of this container and attach panels to it

68setLayout( new BorderLayout() );

69add( innerPanelCenter, BorderLayout.CENTER );

70add( innerPanelSouth, BorderLayout.SOUTH );

71

72// validate layout

73validate();

74

75 } // end constructor

76

77// return reference to generic task button doTask1

78public JButton getDoTask1Button()

79{

80return doTask1;

81}

82

83// return reference to generic task button doTask2

84public JButton getDoTask2Button()

85{

86return doTask2;

87}

88

89// return reference to fields array of JTextFields

90public JTextField[] getFields()

91{

92return fields;

93}

94

95// clear content of text fields

96public void clearFields()

97{

98for ( int count = 0; count < size; count++ )

99

fields[ count ].setText( "" );

Fig. 16.4 BankUI contains a reusable GUI for several programs (part 2 of 3).

906

Files and Streams

Chapter 16

 

 

 

100

}

 

101

 

 

102// set text field values; throw IllegalArgumentException if

103// incorrect number of Strings in argument

104public void setFieldValues( String strings[] )

105throws IllegalArgumentException

106{

107if ( strings.length != size )

108throw new IllegalArgumentException( "There must be " +

109

size + " Strings in the array" );

110

 

111for ( int count = 0; count < size; count++ )

112fields[ count ].setText( strings[ count ] );

113}

114

115// get array of Strings with current text field contents

116public String[] getFieldValues()

117{

118String values[] = new String[ size ];

119

120for ( int count = 0; count < size; count++ )

121values[ count ] = fields[ count ].getText();

123return values;

124}

125

126 } // end class BankUI

Fig. 16.4 BankUI contains a reusable GUI for several programs (part 3 of 3).

Class AccountRecord (Fig. 16.5) implements interface Serializable, which allows objects of AccountRecord to be used with ObjectInputStreams and

ObjectOutputStreams. Interface Serializable is known as a tagging interface. Such an interface contains no methods. A class that implements this interface is tagged as being a Serializable object, which is important because an ObjectOutputStream will not output an object unless it is a Serializable object. In a class that implements Serializable, the programmer must ensure that every instance variable of the class is a Serializable type, or must declare particular instance variables as transient to indicate that those variables are not Serializable and they should be ignored during the serialization process. By default, all primitive type variables are transient. For non-primitive types, you must check the definition of the class (and possibly its superclasses) to ensure that the type is Serializable. Class AccountRecord contains private data members account, firstName, lastName and balance. This class also provides public “get” and “set” methods for accessing the private data members.

1// Fig. 16.5: AccountRecord.java

2 // A class that represents one record of information.

3package com.deitel.jhtp4.ch16;

Fig. 16.5 Class AccountRecord maintains information for one account (part 1 of 3).

Chapter 16

Files and Streams

907

4

5// Java core packages

6 import java.io.Serializable;

7

8 public class AccountRecord implements Serializable {

9private int account;

10private String firstName;

11private String lastName;

12private double balance;

14// no-argument constructor calls other constructor with

15// default values

16public AccountRecord()

17{

18this( 0, "", "", 0.0 );

19}

20

21// initialize a record

22public AccountRecord( int acct, String first,

23String last, double bal )

24{

25setAccount( acct );

26setFirstName( first );

27setLastName( last );

28setBalance( bal );

29}

30

31// set account number

32public void setAccount( int acct )

33{

34account = acct;

35}

36

37// get account number

38public int getAccount()

39{

40return account;

41}

42

43// set first name

44public void setFirstName( String first )

45{

46firstName = first;

47}

48

49// get first name

50public String getFirstName()

51{

52return firstName;

53}

54

Fig. 16.5 Class AccountRecord maintains information for one account (part 2 of 3).

908 Files and Streams

Chapter 16

55// set last name

56public void setLastName( String last )

57{

58lastName = last;

59}

60

61// get last name

62public String getLastName()

63{

64return lastName;

65}

66

67// set balance

68public void setBalance( double bal )

69{

70balance = bal;

71}

72

73// get balance

74public double getBalance()

75{

76return balance;

77}

78

79 } // end class AccountRecord

Fig. 16.5 Class AccountRecord maintains information for one account (part 3 of 3).

Now, let us discuss the code that creates the sequential-access file (Fig. 16.6). In this example, we introduce class JFileChooser (package javax.swing) for selecting files (as on the second screen in Fig. 16.6). Line 103 constructs a JFileChooser instance and assigns it to reference fileChooser. Lines 104–105 call method setFileSelectionMode to specify what the user can select from the fileChooser. For this program, we use JFileChooser static constant FILES_ONLY to indicate that only files can be selected. Other static constants include FILES_AND_DIRECTORIES and

DIRECTORIES_ONLY.

1// Fig. 16.6: CreateSequentialFile.java

2 // Demonstrating object output with class ObjectOutputStream. 3 // The objects are written sequentially to a file.

4

5 // Java core packages

6 import java.io.*;

7import java.awt.*;

8 import java.awt.event.*;

9

10// Java extension packages

11import javax.swing.*;

12

Fig. 16.6 Creating a sequential file (part 1 of 6).

Chapter 16

Files and Streams

909

13// Deitel packages

14import com.deitel.jhtp4.ch16.BankUI;

15import com.deitel.jhtp4.ch16.AccountRecord;

17public class CreateSequentialFile extends JFrame {

18private ObjectOutputStream output;

19private BankUI userInterface;

20private JButton enterButton, openButton;

21

22// set up GUI

23public CreateSequentialFile()

24{

25super( "Creating a Sequential File of Objects" );

27// create instance of reusable user interface

28userInterface = new BankUI( 4 ); // four textfields

29getContentPane().add(

30

userInterface, BorderLayout.CENTER );

31

 

32// get reference to generic task button doTask1 in BankUI

33// and configure button for use in this program

34openButton = userInterface.getDoTask1Button();

35openButton.setText( "Save into File ..." );

36

37// register listener to call openFile when button pressed

38openButton.addActionListener(

39

 

40

// anonymous inner class to handle openButton event

41

new ActionListener() {

42

 

43

// call openFile when button pressed

44

public void actionPerformed( ActionEvent event )

45

{

46

openFile();

47

}

48

 

49

} // end anonymous inner class

50

 

51

); // end call to addActionListener

52

 

53// get reference to generic task button doTask2 in BankUI

54// and configure button for use in this program

55enterButton = userInterface.getDoTask2Button();

56enterButton.setText( "Enter" );

57enterButton.setEnabled( false ); // disable button

58

59// register listener to call addRecord when button pressed

60enterButton.addActionListener(

61

 

62

// anonymous inner class to handle enterButton event

63

new ActionListener() {

64

 

 

 

Fig. 16.6 Creating a sequential file (part 2 of 6).

910 Files and Streams

Chapter 16

65

// call addRecord when button pressed

66

public void actionPerformed( ActionEvent event )

67

{

68

addRecord();

69

}

70

 

71

} // end anonymous inner class

72

 

73

); // end call to addActionListener

74

 

75// register window listener to handle window closing event

76addWindowListener(

77

 

78

// anonymous inner class to handle windowClosing event

79

new WindowAdapter() {

80

 

81

// add current record in GUI to file, then close file

82

public void windowClosing( WindowEvent event )

83

{

84

if ( output != null )

85

addRecord();

86

 

87

closeFile();

88

}

89

 

90

} // end anonymous inner class

91

 

92

); // end call to addWindowListener

93

 

94setSize( 300, 200 );

95show();

96

97 } // end CreateSequentialFile constructor

98

99 // allow user to specify file name

100private void openFile()

101{

102// display file dialog, so user can choose file to open

103JFileChooser fileChooser = new JFileChooser();

104fileChooser.setFileSelectionMode(

105JFileChooser.FILES_ONLY );

106

107 int result = fileChooser.showSaveDialog( this );

108

109// if user clicked Cancel button on dialog, return

110if ( result == JFileChooser.CANCEL_OPTION )

111return;

112

113// get selected file

114File fileName = fileChooser.getSelectedFile();

Fig. 16.6 Creating a sequential file (part 3 of 6).

Chapter 16

Files and Streams

911

116// display error if invalid

117if ( fileName == null ||

118fileName.getName().equals( "" ) )

119JOptionPane.showMessageDialog( this,

120

"Invalid File Name", "Invalid File Name",

121

JOptionPane.ERROR_MESSAGE );

122

 

123

else {

124

 

125// open file

126try {

127

output

= new ObjectOutputStream(

128

new

FileOutputStream( fileName ) );

129

 

 

130

openButton.setEnabled( false );

131enterButton.setEnabled( true );

132}

133

134// process exceptions from opening file

135catch ( IOException ioException ) {

136

JOptionPane.showMessageDialog( this,

137

"Error Opening File", "Error",

138

JOptionPane.ERROR_MESSAGE );

139}

140}

142 } // end method openFile

143

144// close file and terminate application

145private void closeFile()

146{

147// close file

148try {

149output.close();

150

151System.exit( 0 );

152}

153

154// process exceptions from closing file

155catch( IOException ioException ) {

156JOptionPane.showMessageDialog( this,

157

"Error closing file", "Error",

158JOptionPane.ERROR_MESSAGE );

159System.exit( 1 );

160}

161}

162

163// add record to file

164public void addRecord()

165{

166int accountNumber = 0;

167AccountRecord record;

168String fieldValues[] = userInterface.getFieldValues();

Fig. 16.6 Creating a sequential file (part 4 of 6).

912 Files and Streams

Chapter 16

169

170// if account field value is not empty

171if ( ! fieldValues[ BankUI.ACCOUNT ].equals( "" ) ) {

173// output values to file

174try {

175

accountNumber = Integer.parseInt(

176

fieldValues[ BankUI.ACCOUNT ] );

177

 

178

if ( accountNumber > 0 ) {

179

 

180

// create new record

181

record = new AccountRecord( accountNumber,

182

fieldValues[ BankUI.FIRSTNAME ],

183

fieldValues[ BankUI.LASTNAME ],

184

Double.parseDouble(

185

fieldValues[ BankUI.BALANCE ] ) );

186

 

187

// output record and flush buffer

188

output.writeObject( record );

189

output.flush();

190

}

191

 

192

// clear textfields

193userInterface.clearFields();

194}

195

196// process invalid account number or balance format

197catch ( NumberFormatException formatException ) {

198

JOptionPane.showMessageDialog( this,

199

"Bad account number or balance",

200

"Invalid Number Format",

201

JOptionPane.ERROR_MESSAGE );

202

}

203

 

204// process exceptions from file output

205catch ( IOException ioException ) {

206closeFile();

207}

208

209 } // end if

210

211 } // end method addRecord

212

213// execute application; CreateSequentialFile constructor

214// displays window

215public static void main( String args[] )

216{

217new CreateSequentialFile();

218}

219

220 } // end class CreateSequentialFile

Fig. 16.6 Creating a sequential file (part 5 of 6).

Chapter 16

Files and Streams

913

 

 

 

Select location for file here

Click Save to submit new

file name to program

Files and directories are displayed here

BankUI graphical user interface

Fig. 16.6 Creating a sequential file (part 6 of 6).

Line 107 calls method showSaveDialog to display the JFileChooser dialog titled Save. Argument this specifies the JFileChooser dialog’s parent window, which determines the position of the dialog on the screen. If null is passed, the dialog is displayed in the center of the window; otherwise, the dialog is centered over the application window. When displayed, a JFileChooser dialog does not allow the user to interact with any other program window until the user closes the JFileChooser dialog by clicking Save or Cancel. Dialogs that behave in this fashion are called modal dialogs. The user selects the drive, directory and file name, then clicks Save. Method showSaveDialog returns an integer specifying which button the user clicked (Save or Cancel) to close the dialog. Line 110 tests whether the user clicked Cancel by comparing result to static constant CANCEL_OPTION. If so, the method returns.

914 Files and Streams

Chapter 16

Line 114 retrieves the file the user selected by calling method getSelectedFile, which returns an object of type File that encapsulates information about the file (e.g., name and location), but does not represent the contents of the file. This File object does not open the file. We assign this File object to the reference fileName.

As stated previously, a program opens a file by creating an object of stream class FileInputStream or FileOutputStream. In this example, the file is to be opened for output, so the program creates a FileOutputStream. One argument is passed to the FileOutputStream’s constructor—a File object. Existing files opened for output are truncated—all data in the file is discarded.

Common Programming Error 16.1

It is a logic error to open an existing file for output when, in fact, the user wants to preserve the file. The contents of the file are discarded without warning.

Class FileOutputStream provides methods for writing byte arrays and individual bytes to a file. For this program, we need to write objects to a file—a capability not provided by FileOutputStream. The solution to this problem is a technique called chaining of stream objects—the ability to add the services of one stream to another. To chain an ObjectOutputStream to the FileOutputStream, we pass the FileOutputStream object to the ObjectOutputStream’s constructor (lines 127–128). The constructor could throw an IOException if a problem occurs during opening of the file (e.g., when a file is opened for writing on a drive with insufficient space, a read-only file is opened for writing or a nonexistent file is opened for reading). If so, the program displays a JOptionPane. If construction of the two streams does not throw an IOException, the file is open. Then, reference output can be used to write objects to the file.

The program assumes data is input correctly and in the proper record number order. The user populates the JTextFields and clicks Enter to write the data to the file. The Enter button’s actionPerformed method (lines 66–69) calls our method addRecord (lines 164–211) to perform the write operation. Line 188 calls method writeObject to write the record object to file. Line 189 calls method flush to ensure that any data stored in memory is written to the file immediately.

When the user clicks the close box (the X in the window’s top-right corner), the program calls method windowClosing (lines 82–88), which compares output to null (line 84). If output is not null, the stream is open and methods addRecord and closeFile (lines 145–161) are called. Method closeFile calls method close for output to close the file.

Performance Tip 16.3

Always release resources explicitly and at the earliest possible moment at which it is deter- mined that the resource is no longer needed. This makes the resource immediately available to be reused by your program or by another program, thus improving resource utilization.

When using chained stream objects, the outermost object (the ObjectOutputStream in this example) should be used to close the file.

Performance Tip 16.4

Explicitly close each file as soon as it is known that the program will not reference the file again. This can reduce resource usage in a program that will continue executing long after it no longer needs to be referencing a particular file. This practice also improves program clarity.

Chapter 16

Files and Streams

915

In the sample execution for the program of Fig. 16.6, we entered information for five accounts (see Fig. 16.7). The program does not show how the data records actually appear in the file. To verify that the file has been created successfully, in the next section we create a program to read the file.

16.5 Reading Data from a Sequential-Access File

Data are stored in files so that they may be retrieved for processing when needed. The previous section demonstrated how to create a file for sequential access. In this section, we discuss how to read data sequentially from a file.

The program of Fig. 16.8 reads records from a file created by the program of Fig. 16.6 and displays the contents of the records. The program opens the file for input by creating a FileInputStream object. The program specifies the name of the file to open as an argument to the FileInputStream constructor. In Fig. 16.6, we wrote objects to the file, using an ObjectOutputStream object. Data must be read from the file in the same format in which it was written to the file. Therefore, we use an ObjectInputStream chained to a FileInputStream in this program. Note that the third sample screen capture shows the GUI displaying the last record in the file.

Sample Data

100

Bob

Jones

24.98

200

Steve

Doe

-345.67

300

Pam

White

0.00

400

Sam

Stone

-42.16

500

Sue

Rich

224.62

Fig. 16.7 Sample data for the program of Fig. 16.6.

1// Fig. 16.8: ReadSequentialFile.java

2 // This program reads a file of objects sequentially 3 // and displays each record.

4

5 // Java core packages

6 import java.io.*;

7import java.awt.*;

8 import java.awt.event.*;

9

10// Java extension packages

11import javax.swing.*;

12

13// Deitel packages

14import com.deitel.jhtp4.ch16.*;

16public class ReadSequentialFile extends JFrame {

17private ObjectInputStream input;

Fig. 16.8 Reading a sequential file (part 1 of 6).

916 Files and Streams

Chapter 16

18private BankUI userInterface;

19private JButton nextButton, openButton;

21// Constructor -- initialize the Frame

22public ReadSequentialFile()

23{

24super( "Reading a Sequential File of Objects" );

26// create instance of reusable user interface

27userInterface = new BankUI( 4 ); // four textfields

28getContentPane().add(

29

userInterface, BorderLayout.CENTER );

30

 

31// get reference to generic task button doTask1 from BankUI

32openButton = userInterface.getDoTask1Button();

33openButton.setText( "Open File" );

34

35// register listener to call openFile when button pressed

36openButton.addActionListener(

37

 

38

// anonymous inner class to handle openButton event

39

new ActionListener() {

40

 

41

// close file and terminate application

42

public void actionPerformed( ActionEvent event )

43

{

44

openFile();

45

}

46

 

47

} // end anonymous inner class

48

 

49

); // end call to addActionListener

50

 

51// register window listener for window closing event

52addWindowListener(

53

 

54

// anonymous inner class to handle windowClosing event

55

new WindowAdapter() {

56

 

57

// close file and terminate application

58

public void windowClosing( WindowEvent event )

59

{

60

if ( input != null )

61

closeFile();

62

 

63

System.exit( 0 );

64

}

65

 

66

} // end anonymous inner class

67

 

68

); // end call to addWindowListener

69

 

 

 

Fig. 16.8 Reading a sequential file (part 2 of 6).

Chapter 16

Files and Streams

917

70// get reference to generic task button doTask2 from BankUI

71nextButton = userInterface.getDoTask2Button();

72nextButton.setText( "Next Record" );

73nextButton.setEnabled( false );

74

75// register listener to call readRecord when button pressed

76nextButton.addActionListener(

77

 

78

// anonymous inner class to handle nextRecord event

79

new ActionListener() {

80

 

81

// call readRecord when user clicks nextRecord

82

public void actionPerformed( ActionEvent event )

83

{

84

readRecord();

85

}

86

 

87

} // end anonymous inner class

88

 

89

); // end call to addActionListener

90

 

91pack();

92setSize( 300, 200 );

93show();

94

95 } // end ReadSequentialFile constructor

96

97// enable user to select file to open

98private void openFile()

99{

100// display file dialog so user can select file to open

101JFileChooser fileChooser = new JFileChooser();

102fileChooser.setFileSelectionMode(

103JFileChooser.FILES_ONLY );

104

105 int result = fileChooser.showOpenDialog( this );

106

107// if user clicked Cancel button on dialog, return

108if ( result == JFileChooser.CANCEL_OPTION )

109return;

110

111// obtain selected file

112File fileName = fileChooser.getSelectedFile();

114// display error if file name invalid

115if ( fileName == null ||

116fileName.getName().equals( "" ) )

117JOptionPane.showMessageDialog( this,

118

"Invalid File Name", "Invalid File Name",

119

JOptionPane.ERROR_MESSAGE );

120

 

121

else {

122

 

Fig. 16.8 Reading a sequential file (part 3 of 6).

918 Files and Streams

Chapter 16

123// open file

124try {

125

input = new ObjectInputStream(

126

new FileInputStream( fileName ) );

127

 

128

openButton.setEnabled( false );

129nextButton.setEnabled( true );

130}

131

132// process exceptions opening file

133catch ( IOException ioException ) {

134

JOptionPane.showMessageDialog( this,

135

"Error Opening File", "Error",

136

JOptionPane.ERROR_MESSAGE );

137

}

138

 

139

} // end else

140

 

141

} // end method openFile

142

 

143// read record from file

144public void readRecord()

145{

146AccountRecord record;

148// input the values from the file

149try {

150record = ( AccountRecord ) input.readObject();

152// create array of Strings to display in GUI

153String values[] = {

154

String.valueOf(

record.getAccount() ),

155

record.getFirstName(),

156

record.getLastName(),

157

String.valueOf(

record.getBalance() ) };

158

 

 

159// display record contents

160userInterface.setFieldValues( values );

161}

162

163// display message when end-of-file reached

164catch ( EOFException endOfFileException ) {

165nextButton.setEnabled( false );

166

 

167

JOptionPane.showMessageDialog( this,

168

"No more records in file",

169"End of File", JOptionPane.ERROR_MESSAGE );

170}

171

172// display error message if cannot read object

173// because class not found

174catch ( ClassNotFoundException classNotFoundException ) {

175JOptionPane.showMessageDialog( this,

Fig. 16.8 Reading a sequential file (part 4 of 6).

Chapter 16

Files and Streams

919

 

 

 

176

"Unable to create object",

 

177"Class Not Found", JOptionPane.ERROR_MESSAGE );

178}

179

180// display error message if cannot read

181// due to problem with file

182catch ( IOException ioException ) {

183JOptionPane.showMessageDialog( this,

184

"Error during read from file",

185"Read Error", JOptionPane.ERROR_MESSAGE );

186}

187}

188

189// close file and terminate application

190private void closeFile()

191{

192// close file and exit

193try {

194input.close();

195System.exit( 0 );

196}

197

198// process exception while closing file

199catch ( IOException ioException ) {

200JOptionPane.showMessageDialog( this,

201

"Error closing file",

202

"Error", JOptionPane.ERROR_MESSAGE );

203

 

204System.exit( 1 );

205}

206}

207

208// execute application; ReadSequentialFile constructor

209// displays window

210public static void main( String args[] )

211{

212new ReadSequentialFile();

213}

214

215 } // end class ReadSequentialFile

Fig. 16.8 Reading a sequential file (part 5 of 6).

920 Files and Streams

Chapter 16

 

 

 

 

Fig. 16.8 Reading a sequential file (part 6 of 6).

Most of the code in this example is similar to Fig. 16.6, so we discuss only the key lines of code that are different. Line 105 calls JFileChooser method showOpenDialog to display the Open dialog (second screen capture in Fig. 16.8). The behavior and GUI are the same as the dialog displayed by showSaveDialog, except that the title of the dialog and the Save button are both replaced with Open.

Lines 125–126 create a ObjectInputStream object and assign it to input. The File fileName is passed to the FileInputStream constructor to open the file.

The program reads a record from the file each time the user clicks the Next Record button. Line 84 in Next Record’s actionPerformed method calls method readRecord (lines 144–187) to read one record from the file. Line 150 calls method readObject to read an Object from the ObjectInputStream. To use AccountRecord specific methods, we cast the returned Object to type AccountRecord. If the end-of-file marker is reached during reading, readObject throws an EndOfFileException.

To retrieve data sequentially from a file, programs normally start reading from the beginning of the file and read all the data consecutively until the desired data are found. It might be necessary to process the file sequentially several times (from the beginning of the file) during the execution of a program. Class FileInputStream does not provide the ability to reposition to the beginning of the file to read the file again unless the program closes the file and reopens it. Class RandomAccessFile objects can reposition to the beginning of the file. Class RandomAccessFile provides all the capabilities of the

Chapter 16

Files and Streams

921

classes FileInputStream, FileOutputStream, DataInputStream and

DataOutputStream and adds several other methods, including a seek that repositions the file-position pointer (the byte number of the next byte in the file to be read or written) to any position in the file. However, class RandomAccessFile cannot read and write entire objects.

Performance Tip 16.5

The process of closing and reopening a file for the purpose of positioning the file-position pointer back to the beginning of a file is a time-consuming task for the computer. If this is done frequently, it can slow the performance of your program.

The program of Fig. 16.9 enables a credit manager to display the account information for those customers with zero balances (i.e., customers who do not owe the company any money), credit balances (i.e., customers to whom the company owes money) and debit balances (i.e., customers who owe the company money for goods and services received in the past).

The program displays buttons that allow a credit manager to obtain credit information. The Credit balances button produces a list of accounts with credit balances. The Debit balances button produces a list of accounts with debit balances. The Zero balances button produces a list of accounts with zero balances.

Records are displayed in a JTextArea called recordDisplayArea. The record information is collected by reading through the entire file and determining, for each record, whether it satisfies the criteria for the account type selected by the credit manager. Clicking one of the balance buttons sets variable accountType (line 274) to the clicked button’s text (e.g., Zero balances) and invokes method readRecords (191–238), which loops through the file and reads every record. Line 208 of method readRecords calls method shouldDisplay (241–259) to determine whether the current record satisfies the account type requested. If shouldDisplay returns true, the program appends the account information for the current record to the JTextArea recordDisplay. When the end- of-file marker is reached, line 219 calls method closeFile to close the file.

1// Fig. 16.9: CreditInquiry.java

2 // This program reads a file sequentially and displays the 3 // contents in a text area based on the type of account the 4 // user requests (credit balance, debit balance or

5 // zero balance).

6

7 // Java core packages

8 import java.io.*;

9import java.awt.*;

10import java.awt.event.*;

11import java.text.DecimalFormat;

13// Java extension packages

14import javax.swing.*;

15

16// Deitel packages

17import com.deitel.jhtp4.ch16.AccountRecord;

Fig. 16.9 Credit inquiry program (part 1 of 7).

922 Files and Streams

Chapter 16

18

19public class CreditInquiry extends JFrame {

20private JTextArea recordDisplayArea;

21private JButton openButton,

22creditButton, debitButton, zeroButton;

23private JPanel buttonPanel;

24

25private ObjectInputStream input;

26private FileInputStream fileInput;

27private File fileName;

28private String accountType;

29

30// set up GUI

31public CreditInquiry()

32{

33super( "Credit Inquiry Program" );

35 Container container = getContentPane();

36

37// set up panel for buttons

38buttonPanel = new JPanel();

40// create and configure button to open file

41openButton = new JButton( "Open File" );

42buttonPanel.add( openButton );

43

44// register openButton listener

45openButton.addActionListener(

47

//

anonymous inner class to handle openButton event

48

new ActionListener() {

49

 

 

50

 

// open file for processing

51

 

public void actionPerformed( ActionEvent event )

52

 

{

53

 

openFile( true );

54

 

}

55

 

 

56

}

// end anonymous inner class

57

 

 

58

); // end call to addActionListener

59

 

 

60// create and configure button to get

61// accounts with credit balances

62creditButton = new JButton( "Credit balances" );

63buttonPanel.add( creditButton );

64creditButton.addActionListener( new ButtonHandler() );

66// create and configure button to get

67// accounts with debit balances

68debitButton = new JButton( "Debit balances" );

69buttonPanel.add( debitButton );

70debitButton.addActionListener( new ButtonHandler() );

Fig. 16.9 Credit inquiry program (part 2 of 7).

Chapter 16

Files and Streams

923

71

72// create and configure button to get

73// accounts with credit balances

74zeroButton = new JButton( "Zero balances" );

75buttonPanel.add( zeroButton );

76zeroButton.addActionListener( new ButtonHandler() );

78// set up display area

79recordDisplayArea = new JTextArea();

80JScrollPane scroller =

81

new JScrollPane( recordDisplayArea );

82

 

83// attach components to content pane

84container.add( scroller, BorderLayout.CENTER );

85container.add( buttonPanel, BorderLayout.SOUTH );

87// disable creditButton, debitButton and zeroButton

88creditButton.setEnabled( false );

89debitButton.setEnabled( false );

90zeroButton.setEnabled( false );

91

92// register window listener

93addWindowListener(

94

 

95

// anonymous inner class for windowClosing event

96

new WindowAdapter() {

97

 

98

// close file and terminate program

99

public void windowClosing( WindowEvent event )

100

{

101

closeFile();

102

System.exit( 0 );

103

}

104

 

105

} // end anonymous inner class

106

 

107

); // end call to addWindowListener

108

 

109// pack components and display window

110pack();

111setSize( 600, 250 );

112show();

113

114 } // end CreditInquiry constructor

115

116// enable user to choose file to open first time;

117// otherwise, reopen chosen file

118private void openFile( boolean firstTime )

119{

120if ( firstTime ) {

121

122// display dialog, so user can choose file

123JFileChooser fileChooser = new JFileChooser();

Fig. 16.9 Credit inquiry program (part 3 of 7).

924

Files and Streams

Chapter 16

 

 

 

124

fileChooser.setFileSelectionMode(

 

125

JFileChooser.FILES_ONLY );

 

126

 

 

127

int result = fileChooser.showOpenDialog( this );

128

 

 

129// if user clicked Cancel button on dialog, return

130if ( result == JFileChooser.CANCEL_OPTION )

131

return;

132

 

133// obtain selected file

134fileName = fileChooser.getSelectedFile();

135}

136

137// display error if file name invalid

138if ( fileName == null ||

139fileName.getName().equals( "" ) )

140JOptionPane.showMessageDialog( this,

141

"Invalid File Name", "Invalid File Name",

142

JOptionPane.ERROR_MESSAGE );

143

 

144

else {

145

 

146// open file

147try {

148

 

149

// close file from previous operation

150

if ( input != null )

151

input.close();

152

 

153

fileInput = new FileInputStream( fileName );

154

input = new ObjectInputStream( fileInput );

155

openButton.setEnabled( false );

156

creditButton.setEnabled( true );

157

debitButton.setEnabled( true );

158zeroButton.setEnabled( true );

159}

160

161// catch problems manipulating file

162catch ( IOException ioException ) {

163

JOptionPane.showMessageDialog( this,

164

"File does not exist", "Invalid File Name",

165

JOptionPane.ERROR_MESSAGE );

166}

167}

169 } // end method openFile

170

171// close file before application terminates

172private void closeFile()

173{

174// close file

175try {

176input.close();

Fig. 16.9 Credit inquiry program (part 4 of 7).

Chapter 16

Files and Streams

925

 

 

 

 

177

}

 

 

178

 

 

 

179// process exception from closing file

180catch ( IOException ioException ) {

181JOptionPane.showMessageDialog( this,

182

"Error closing file",

183

"Error", JOptionPane.ERROR_MESSAGE );

184

 

185System.exit( 1 );

186}

187}

188

189// read records from file and display only records of

190// appropriate type

191private void readRecords()

192{

193AccountRecord record;

194DecimalFormat twoDigits = new DecimalFormat( "0.00" );

195openFile( false );

196

197// read records

198try {

199recordDisplayArea.setText( "The accounts are:\n" );

201// input the values from the file

202while ( true ) {

203

 

204

// read one AccountRecord

205

record = ( AccountRecord ) input.readObject();

206

 

207

// if proper acount type, display record

208

if ( shouldDisplay( record.getBalance() ) )

209

recordDisplayArea.append( record.getAccount() +

210

"\t" + record.getFirstName() + "\t" +

211

record.getLastName() + "\t" +

212

twoDigits.format( record.getBalance() ) +

213

"\n" );

214}

215}

217// close file when end-of-file reached

218catch ( EOFException eofException ) {

219closeFile();

220}

221

222// display error if cannot read object

223// because class not found

224catch ( ClassNotFoundException classNotFound ) {

225JOptionPane.showMessageDialog( this,

226

"Unable to create object",

227"Class Not Found", JOptionPane.ERROR_MESSAGE );

228}

229

Fig. 16.9 Credit inquiry program (part 5 of 7).

926 Files and Streams

Chapter 16

230// display error if cannot read

231// because problem with file

232catch ( IOException ioException ) {

233JOptionPane.showMessageDialog( this,

234

"Error reading from file",

235"Error", JOptionPane.ERROR_MESSAGE );

236}

237

238 } // end method readRecords

239

240// uses record ty to determine if a record should be displayed

241private boolean shouldDisplay( double balance )

242{

243if ( accountType.equals( "Credit balances" ) &&

244balance < 0 )

245

246 return true;

247

248else if ( accountType.equals( "Debit balances" ) &&

249balance > 0 )

250

251 return true;

252

253else if ( accountType.equals( "Zero balances" ) &&

254balance == 0 )

255

256 return true;

257

258return false;

259}

260

261// execute application

262public static void main( String args[] )

263{

264new CreditInquiry();

265}

266

267// private inner class for creditButton, debitButton and

268// zeroButton event handling

269private class ButtonHandler implements ActionListener {

271// read records from file

272public void actionPerformed( ActionEvent event )

273{

274accountType = event.getActionCommand();

275readRecords();

276}

277

278 } // end class ButtonHandler

279

280 } // end class CreditInquiry

Fig. 16.9 Credit inquiry program (part 6 of 7).

Chapter 16

Files and Streams

927

 

 

 

 

 

 

Fig. 16.9 Credit inquiry program (part 7 of 7).

16.6 Updating Sequential-Access Files

Data that is formatted and written to a sequential-access file as shown in Section 16.4 cannot be modified without reading and writing all the data in the file. For example, if the name White needed to be changed to Worthington, the old name cannot simply be overwritten. Such updating can be done, but it is awkward. To make the preceding name change, the records before White in a sequential-access file could be copied to a new file, the updated record would then be written to the new file, and the records after White would be copied to the new file. This requires processing every record in the file to update one record. If many records are being updated in one pass of the file, this technique can be acceptable.

928 Files and Streams

Chapter 16

16.7 Random-Access Files

So far, we have seen how to create sequential-access files and to search through them to locate particular information. Sequential-access files are inappropriate for so-called “in- stant-access” applications, in which a particular record of information must be located immediately. Some popular instant-access applications are airline reservation systems, banking systems, point-of-sale systems, automated-teller machines and other kinds of transaction-processing systems that require rapid access to specific data. The bank at which you have your account might have hundreds of thousands or even millions of other customers, yet, when you use an automated teller machine, 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. A program can access individual records of a randomaccess file directly (and quickly) without searching through other records. Random-access files are sometimes called direct-access files.

As we have said, Java does not impose structure on a file, so an application that wants to use random-access files must create them. Several techniques can be used to create random-access files. Perhaps the simplest is to require that all 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 will soon see how this facilitates immediate access to specific records, even in large files.

Figure 16.10 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 cars—some empty, some with contents.

A program can insert data in a random-access file without destroying other data in the file. Also, 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 data no longer needed.

16.8 Creating a Random-Access File

RandomAccessFile objects have all the capabilities of DataInputStream and DataOutputStream objects discussed earlier. 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 and manipulates all data as primitive data types. When writing an int value, 4 bytes are output to the file. When reading a double value, 8 bytes are input from the file. The size of the data types is guaranteed, because Java has fixed sizes for all primitive data 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 following examples.

Consider the following problem statement:

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.

Chapter 16

Files and Streams

929

0

100

200

300

400

500

byte offsets

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

100

 

100

 

100

 

100

 

100

 

100

 

 

bytes

bytes

bytes

bytes

bytes

bytes

 

Fig. 16.10 Java’s view of a random-access file.

The next several sections introduce the techniques necessary to create this credit-pro- cessing program. Figure 16.11 contains the RandomAccessAccountRecord class that is used by the next four programs for both reading records from and writing records to a file.

1// Fig. 16.11: RandomAccessAccountRecord.java

2 // Subclass of AccountRecord for random access file programs. 3 package com.deitel.jhtp4.ch16;

4

5 // Java core packages

6 import java.io.*;

7

8 public class RandomAccessAccountRecord extends AccountRecord {

9

10// no-argument constructor calls other constructor

11// with default values

12public RandomAccessAccountRecord()

13{

14this( 0, "", "", 0.0 );

15}

16

17// initialize a RandomAccessAccountRecord

18public RandomAccessAccountRecord( int account,

19String firstName, String lastName, double balance )

20{

21super( account, firstName, lastName, balance );

22}

23

24// read a record from specified RandomAccessFile

25public void read( RandomAccessFile file ) throws IOException

26{

27setAccount( file.readInt() );

28setFirstName( padName( file ) );

29setLastName( padName( file ) );

30setBalance( file.readDouble() );

31}

32

Fig. 16.11 RandomAccessAccountRecord class used in the random-access file programs (part 1 of 2).

930 Files and Streams

Chapter 16

33// ensure that name is proper length

34private String padName( RandomAccessFile file )

35throws IOException

36{

37char name[] = new char[ 15 ], temp;

38

 

39

for ( int count = 0; count < name.length; count++ ) {

40

temp = file.readChar();

41name[ count ] = temp;

42}

43

44return new String( name ).replace( '\0', ' ' );

45}

46

47// write a record to specified RandomAccessFile

48public void write( RandomAccessFile file ) throws IOException

49{

50file.writeInt( getAccount() );

51writeName( file, getFirstName() );

52writeName( file, getLastName() );

53file.writeDouble( getBalance() );

54}

55

56// write a name to file; maximum of 15 characters

57private void writeName( RandomAccessFile file, String name )

58throws IOException

59{

60StringBuffer buffer = null;

61

62 if ( name != null )

63buffer = new StringBuffer( name );

64else

65

buffer = new StringBuffer( 15 );

66

 

67buffer.setLength( 15 );

68file.writeChars( buffer.toString() );

69}

70

71// NOTE: This method contains a hard coded value for the

72// size of a record of information.

73public static int size()

74{

75return 72;

76}

77

78 } // end class RandomAccessAccountRecord

Fig. 16.11 RandomAccessAccountRecord class used in the random-access file programs (part 2 of 2).

Class RandomAccessAccountRecord inherits AccountRecord’s (Fig. 16.5) implementation, which includes private instance variables—account, lastName, firstName and balance—as well as their public set and get methods.

Chapter 16

Files and Streams

931

Method read (lines 25–31) reads one record from the RandomAccessFile object passed as an argument. Methods readInt (line 27) and readDouble (line 30) read the account and balance, respectively. Method read calls private method padName (lines 34–45) twice to obtain the first and last names. Method padName reads fifteen characters from the RandomAccessFile and returns a String. If a name is shorter than 15 characters, the program fills each extra character with a null byte ('\0'). Swing components, such as JTextFields, cannot display null byte characters (which are displayed instead as rectangles). Line 44 solves this problem by replacing null bytes with spaces.

Method write (48–54) outputs one record to the RandomAccessFile object passed in as an argument. This method uses method writeInt to output the integer account, method writeChars (called from utility method writeName) to output the firstName and lastName character arrays and method writeDouble to output the double balance. [Note: In order to ensure that all records in the RandomAccessFile have the same size, we write exactly 15 characters for the first name and exactly 15 characters for the last name.] Method writeName (lines 57–69) performs the write operations for the first and last name.

Figure 16.12 illustrates opening a random-access file and writing data to the disk. This program writes 100 RandomAccessAccountRecords, using method write (Fig. 16.11). 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.

1// Fig. 16.12: CreateRandomFile.java

2 // This program creates a random access file sequentially 3 // by writing 100 empty records to disk.

4

5 // Java core packages

6 import java.io.*;

7

8 // Java extension packages

9 import javax.swing.*;

10

11// Deitel packages

12import com.deitel.jhtp4.ch16.RandomAccessAccountRecord;

14 public class CreateRandomFile {

15

16// enable user to select file to open

17private void createFile()

18{

19// display dialog so user can choose file

20JFileChooser fileChooser = new JFileChooser();

21fileChooser.setFileSelectionMode(

22

JFileChooser.FILES_ONLY );

23

 

24

int result = fileChooser.showSaveDialog( null );

 

 

Fig. 16.12

Creating a random-access file sequentially (part 1 of 3).

932 Files and Streams

Chapter 16

25

26// if user clicked Cancel button on dialog, return

27if ( result == JFileChooser.CANCEL_OPTION )

28

return;

29

 

30// obtain selected file

31File fileName = fileChooser.getSelectedFile();

33// display error if file name invalid

34if ( fileName == null ||

35

fileName.getName().equals( "" ) )

36

JOptionPane.showMessageDialog( null,

37

"Invalid File Name", "Invalid File Name",

38

JOptionPane.ERROR_MESSAGE );

39

 

40

else {

41

 

42

// open file

43

try {

44

RandomAccessFile file =

45

new RandomAccessFile( fileName, "rw" );

46

 

47

RandomAccessAccountRecord blankRecord =

48

new RandomAccessAccountRecord();

49

 

50

// write 100 blank records

51

for ( int count = 0; count < 100; count++ )

52

blankRecord.write( file );

53

 

54

// close file

55

file.close();

56

 

57

// display message that file was created

58

JOptionPane.showMessageDialog( null,

59

"Created file " + fileName, "Status",

60

JOptionPane.INFORMATION_MESSAGE );

61

 

62

System.exit( 0 ); // terminate program

63

}

64

 

65

// process exceptions during open, write or

66

// close file operations

67

catch ( IOException ioException ) {

68

JOptionPane.showMessageDialog( null,

69

"Error processing file", "Error processing file",

70

JOptionPane.ERROR_MESSAGE );

71

 

72

System.exit( 1 );

73}

74}

75

76 } // end method openFile

77

Fig. 16.12 Creating a random-access file sequentially (part 2 of 3).

Chapter 16

Files and Streams

933

78// execute application to create file user specifies

79public static void main( String args[] )

80{

81CreateRandomFile application = new CreateRandomFile();

83application.createFile();

84}

85

86 } // end class CreateRandomFile

Fig. 16.12 Creating a random-access file sequentially (part 3 of 3).

Lines 44–45 attempt to open a RandomAccessFile for use in this program. The RandomAccessFile constructor receives two arguments—the 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.

If an IOException occurs during the open process, the program displays a message dialog and terminates. If the file opens properly, the program uses a for structure (lines 51–52) to invoke RandomAccessAccountRecord method write 100 times. This statement causes the data members of object blankRecord to be written to the file associated with RandomAccessFile object file.

16.9 Writing Data Randomly to a Random-Access File

Figure 16.13 writes data to a file that is opened with the "rw" mode for reading and writing. It uses the RandomAccessFile method seek to determine the exact location in the file at which a record of information is stored. Method seek sets the file-position pointer to a specific position in the file relative to the beginning of the file, and the RandomAccessAccountRecord class method write outputs the data. This program assumes

934 Files and Streams

Chapter 16

the user does not enter duplicate account numbers and that the user enters appropriate data in each JTextField.

1// Fig. 16.13: WriteRandomFile.java

2 // This program uses textfields to get information from the 3 // user at the keyboard and writes the information to a

4 // random-access file.

5

6 // Java core packages

7import java.awt.*;

8 import java.awt.event.*;

9 import java.io.*;

10

11// Java extension packages

12import javax.swing.*;

13

14// Deitel packages

15import com.deitel.jhtp4.ch16.*;

17public class WriteRandomFile extends JFrame {

18private RandomAccessFile output;

19private BankUI userInterface;

20private JButton enterButton, openButton;

22// set up GUI

23public WriteRandomFile()

24{

25super( "Write to random access file" );

27// create instance of reusable user interface BankUI

28userInterface = new BankUI( 4 ); // four textfields

29getContentPane().add( userInterface,

30

BorderLayout.CENTER );

31

 

32// get reference to generic task button doTask1 in BankUI

33openButton = userInterface.getDoTask1Button();

34openButton.setText( "Open..." );

35

36// register listener to call openFile when button pressed

37openButton.addActionListener(

38

 

39

// anonymous inner class to handle openButton event

40

new ActionListener() {

41

 

42

// allow user to select file to open

43

public void actionPerformed( ActionEvent event )

44

{

45

openFile();

46

}

47

 

48

} // end anonymous inner class

49

 

50

); // end call to addActionListener

 

 

Fig. 16.13

Writing data randomly to a random-access file (part 1 of 5).

Chapter 16

Files and Streams

935

51

52// register window listener for window closing event

53addWindowListener(

54

 

55

// anonymous inner class to handle windowClosing event

56

new WindowAdapter() {

57

 

58

// add record in GUI, then close file

59

public void windowClosing( WindowEvent event )

60

{

61

if ( output != null )

62

addRecord();

63

 

64

closeFile();

65

}

66

 

67

} // end anonymous inner class

68

 

69

); // end call to addWindowListener

70

 

71// get reference to generic task button doTask2 in BankUI

72enterButton = userInterface.getDoTask2Button();

73enterButton.setText( "Enter" );

74enterButton.setEnabled( false );

75

76// register listener to call addRecord when button pressed

77enterButton.addActionListener(

78

 

79

// anonymous inner class to handle enterButton event

80

new ActionListener() {

81

 

82

// add record to file

83

public void actionPerformed( ActionEvent event )

84

{

85

addRecord();

86

}

87

 

88

} // end anonymous inner class

89

 

90

); // end call to addActionListener

91

 

92setSize( 300, 150 );

93show();

94}

95

96// enable user to choose file to open

97private void openFile()

98{

99// display file dialog so user can select file 100 JFileChooser fileChooser = new JFileChooser();

101fileChooser.setFileSelectionMode(

102JFileChooser.FILES_ONLY );

103

Fig. 16.13 Writing data randomly to a random-access file (part 2 of 5).

936 Files and Streams

Chapter 16

104 int result = fileChooser.showOpenDialog( this );

105

106// if user clicked Cancel button on dialog, return

107if ( result == JFileChooser.CANCEL_OPTION )

108return;

109

110// obtain selected file

111File fileName = fileChooser.getSelectedFile();

113// display error if file name invalid

114if ( fileName == null ||

115fileName.getName().equals( "" ) )

116JOptionPane.showMessageDialog( this,

117

"Invalid File Name", "Invalid File Name",

118

JOptionPane.ERROR_MESSAGE );

119

 

120

else {

121

 

122// open file

123try {

124

output = new RandomAccessFile( fileName, "rw" );

125

enterButton.setEnabled( true );

126openButton.setEnabled( false );

127}

128

129// process exception while opening file

130catch ( IOException ioException ) {

131

JOptionPane.showMessageDialog( this,

132

"File does not exist",

133

"Invalid File Name",

134

JOptionPane.ERROR_MESSAGE );

135}

136}

138 } // end method openFile

139

140// close file and terminate application

141private void closeFile()

142{

143// close file and exit

144try {

145if ( output != null )

146

output.close();

147

 

148System.exit( 0 );

149}

150

151// process exception while closing file

152catch( IOException ioException ) {

153JOptionPane.showMessageDialog( this,

154

"Error closing file",

155

"Error", JOptionPane.ERROR_MESSAGE );

156

 

 

 

Fig. 16.13

Writing data randomly to a random-access file (part 3 of 5).

Chapter 16

Files and Streams

937

157System.exit( 1 );

158}

159}

160

161// add one record to file

162public void addRecord()

163{

164int accountNumber = 0;

165String fields[] = userInterface.getFieldValues();

166RandomAccessAccountRecord record =

167new RandomAccessAccountRecord();

168

169// ensure account field has a value

170if ( ! fields[ BankUI.ACCOUNT ].equals( "" ) ) {

172// output values to file

173try {

174

accountNumber =

175

Integer.parseInt( fields[ BankUI.ACCOUNT ] );

176

 

177

if ( accountNumber > 0 && accountNumber <= 100 ) {

178

record.setAccount( accountNumber );

179

 

180

record.setFirstName( fields[ BankUI.FIRSTNAME ] );

181

record.setLastName( fields[ BankUI.LASTNAME ] );

182

record.setBalance( Double.parseDouble(

183

fields[ BankUI.BALANCE ] ) );

184

 

185

output.seek( ( accountNumber - 1 ) *

186

RandomAccessAccountRecord.size() );

187

record.write( output );

188

}

189

 

190userInterface.clearFields(); // clear TextFields

191}

192

193// process improper account number or balance format

194catch ( NumberFormatException formatException ) {

195

JOptionPane.showMessageDialog( this,

196

"Bad account number or balance",

197

"Invalid Number Format",

198

JOptionPane.ERROR_MESSAGE );

199

}

200

 

201// process exceptions while writing to file

202catch ( IOException ioException ) {

203closeFile();

204}

205}

206

207 } // end method addRecord

208

Fig. 16.13 Writing data randomly to a random-access file (part 4 of 5).

938 Files and Streams

Chapter 16

209// execute application

210public static void main( String args[] )

211{

212new WriteRandomFile();

213}

214

215 } // end class WriteRandomFile

Fig. 16.13 Writing data randomly to a random-access file (part 5 of 5).

The user enters values for the account number, first name, last name and balance. When the user clicks the Enter button, the program calls method addRecord (162–207) of class WriteRandomFile to retrieve the data from the BankAccountUI’s JTextFields, store the data in RandomAccessAccountRecord class object record and call the write method of class RandomAccessAccountRecord to output the data.

Lines 185–186 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 should be between 1 and 100. We subtract 1 from the account number when calculating the byte location of the record. Thus, for record 1, the file-position pointer is set to byte 0 of the file.

Chapter 16

Files and Streams

939

When the user closes the window, the program attempts to add the last record to the file (if there is one in the GUI waiting to be output), closes the file and terminates.

16.10 Reading Data Sequentially from a Random-Access File

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

Good Programming Practice 16.1

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

1// Fig. 16.14: 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

5 // Java core packages

6import java.awt.*;

7 import java.awt.event.*;

8import java.io.*;

9 import java.text.DecimalFormat;

10

11// Java extension packages

12import javax.swing.*;

13

14// Deitel packages

15import com.deitel.jhtp4.ch16.*;

17public class ReadRandomFile extends JFrame {

18private BankUI userInterface;

19private RandomAccessFile input;

20private JButton nextButton, openButton;

22// set up GUI

23public ReadRandomFile()

24{

25super( "Read Client File" );

27// create reusable user interface instance

28userInterface = new BankUI( 4 ); // four textfields

29getContentPane().add( userInterface );

30

31// configure generic doTask1 button from BankUI

32openButton = userInterface.getDoTask1Button();

33openButton.setText( "Open File for Reading..." );

Fig. 16.14 Reading a random-access file sequentially (part 1 of 5).

940 Files and Streams

Chapter 16

35// register listener to call openFile when button pressed

36openButton.addActionListener(

37

 

38

// anonymous inner class to handle openButton event

39

new ActionListener() {

40

 

41

// enable user to select file to open

42

public void actionPerformed( ActionEvent event )

43

{

44

openFile();

45

}

46

 

47

} // end anonymous inner class

48

 

49

); // end call to addActionListener

50

 

51// configure generic doTask2 button from BankUI

52nextButton = userInterface.getDoTask2Button();

53nextButton.setText( "Next" );

54nextButton.setEnabled( false );

55

56// register listener to call readRecord when button pressed

57nextButton.addActionListener(

58

 

59

// anonymous inner class to handle nextButton event

60

new ActionListener() {

61

 

62

// read a record when user clicks nextButton

63

public void actionPerformed( ActionEvent event )

64

{

65

readRecord();

66

}

67

 

68

} // end anonymous inner class

69

 

70

); // end call to addActionListener

71

 

72// register listener for window closing event

73addWindowListener(

74

 

75

// anonymous inner class to handle windowClosing event

76

new WindowAdapter() {

77

 

78

// close file and terminate application

79

public void windowClosing( WindowEvent event )

80

{

81

closeFile();

82

}

83

 

84

} // end anonymous inner class

85

 

86

); // end call to addWindowListener

87

 

 

 

Fig. 16.14

Reading a random-access file sequentially (part 2 of 5).

Chapter 16

Files and Streams

941

88setSize( 300, 150 );

89show();

90}

91

92// enable user to select file to open

93private void openFile()

94{

95// display file dialog so user can select file

96JFileChooser fileChooser = new JFileChooser();

97fileChooser.setFileSelectionMode(

98

JFileChooser.FILES_ONLY );

99

 

100

int result = fileChooser.showOpenDialog( this );

101

 

102// if user clicked Cancel button on dialog, return

103if ( result == JFileChooser.CANCEL_OPTION )

104return;

105

106// obtain selected file

107File fileName = fileChooser.getSelectedFile();

109// display error is file name invalid

110if ( fileName == null ||

111fileName.getName().equals( "" ) )

112JOptionPane.showMessageDialog( this,

113

"Invalid File Name", "Invalid File Name",

114

JOptionPane.ERROR_MESSAGE );

115

 

116

else {

117

 

118// open file

119try {

120

input = new RandomAccessFile( fileName, "r" );

121

nextButton.setEnabled( true );

122openButton.setEnabled( false );

123}

124

125// catch exception while opening file

126catch ( IOException ioException ) {

127

JOptionPane.showMessageDialog( this,

128

"File does not exist", "Invalid File Name",

129

JOptionPane.ERROR_MESSAGE );

130}

131}

133 } // end method openFile

134

135// read one record

136public void readRecord()

137{

138DecimalFormat twoDigits = new DecimalFormat( "0.00" );

139RandomAccessAccountRecord record =

140new RandomAccessAccountRecord();

Fig. 16.14 Reading a random-access file sequentially (part 3 of 5).

942 Files and Streams

Chapter 16

141

142// read a record and display

143try {

144

 

145

do {

146record.read( input );

147} while ( record.getAccount() == 0 );

149

String values[] = {

150

String.valueOf( record.getAccount() ),

151

record.getFirstName(),

152

record.getLastName(),

153String.valueOf( record.getBalance() ) };

154userInterface.setFieldValues( values );

155}

156

157// close file when end-of-file reached

158catch ( EOFException eofException ) {

159JOptionPane.showMessageDialog( this, "No more records",

160

"End-of-file reached",

161JOptionPane.INFORMATION_MESSAGE );

162closeFile();

163}

164

165// process exceptions from problem with file

166catch ( IOException ioException ) {

167JOptionPane.showMessageDialog( this,

168

"Error Reading File", "Error",

169

JOptionPane.ERROR_MESSAGE );

170

 

171System.exit( 1 );

172}

173

174 } // end method readRecord

175

176// close file and terminate application

177private void closeFile()

178{

179// close file and exit

180try {

181if ( input != null )

182

input.close();

183

 

184System.exit( 0 );

185}

186

187// process exception closing file

188catch( IOException ioException ) {

189JOptionPane.showMessageDialog( this,

190

"Error closing file",

191

"Error", JOptionPane.ERROR_MESSAGE );

192

 

193

System.exit( 1 );

 

 

Fig. 16.14

Reading a random-access file sequentially (part 4 of 5).

Chapter 16

Files and Streams

943

194}

195}

197// execute application

198public static void main( String args[] )

199{

200new ReadRandomFile();

201}

202

203 } // end class ReadRandomFile

Fig. 16.14 Reading a random-access file sequentially (part 5 of 5).

When the user clicks the Next button to read the next record in the file, the program invokes class ReadRandomFile’s readRecord method (lines 136–174). This method invokes class RandomAccessAccountRecord’s read method (line 146) to read the data into RandomAccessAccountRecord class object record. Method readRecord reads from the file until it encounters a record with a nonzero account number (zero is the initial value for the account). When readRecord encounters a valid account number (i.e., a nonzero value), the loop terminates, and readRecord displays the record data in the text fields. When the user clicks the Done button or when the end-of-file

944 Files and Streams

Chapter 16

marker is encountered while reading, method closeFile is invoked to close the file and terminate the program.

What about that additional benefit we promised? If you examine the GUI as the program executes, you will notice that the records are displayed in sorted order (by account number)! This is a simple consequence of the way we stored these records in the file, using direct-access techniques. Compared to the bubble sort we have seen (Chapter 7), 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, of course, means that the file could be sparsely occupied most of the time, a waste of storage. So this 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.

16.11 Example: A Transaction-Processing Program

We now present a substantial transaction-processing program (Fig. 16.20), using a randomaccess file to achieve “instant” access processing. The program maintains a bank’s account information. The program updates existing accounts, adds new accounts and deletes accounts. We assume that the program of Fig. 16.12 has been executed to create a file and that the program of Fig. 16.13 has been executed to insert initial data. The techniques used in this example have been presented in the earlier RandomAccessFile examples.

This program GUI consists of a window with a menu bar containing a File menu and internal frames that enable the user to perform insert, update and delete record operations on the file. The internal frames are subclasses of JInternalFrame that are managed by a JDesktopPane (as discussed in Chapter 13). The File menu has five menu items to select various tasks, as shown in Fig. 16.15.

When the user selects Update Record from the File menu, the Update Record internal frame (Fig. 16.16) allows the user to update an existing account. The code that implements the Update Record internal frame is in class UpdateDialog (lines 238–458 of Fig. 16.20). In the first screen capture of Fig. 16.16, the user inputs an account number and presses Enter to invoke the account textfield’s actionPerformed method (lines 345–360 of Fig. 16.20). This reads the account from the file with method getRecord (lines 371–418 of Fig. 16.20), which validates the account number, then reads the record with RandomAccessAccountRecord method read. Next, getRecord compares the account number with zero (i.e., no record) to determine whether the record contains information. If not, getRecord displays a message stating that the record does not exist; otherwise, it returns the record. Lines 350–357 of the account textfield’s actionPerformed method extract the account information from the record and display the account information in the internal frame (as shown in the second screen capture of Fig. 16.16). The Transaction amount textfield initially contains the string charge (+) or payment (–). The user should select this text and type the transaction amount (a positive value for a charge or a negative value for a payment), then press Enter to invoke the transaction textfield’s actionPerformed method (lines 295–330 of Fig. 16.20). Method addRecord (lines 421–456 of Fig. 16.20) takes the transaction amount, adds it to the current balance and calls RandomAccessAccountRecord method setBalance to update the display. Clicking Save Changes writes the updated record to disk; clicking Cancel closes the internal frame without writing the record to disk. The windows in Fig. 16.17 show a sample of a transaction being input.

Chapter 16 Files and Streams 945

When the user selects New Record from the File menu, the New Record internal frame in Fig. 16.18 allows the user to add a new record. The code that implements the New Record internal frame is in class NewDialog (lines 461–615 of Fig. 16.20). The user enters data in the JTextFields and clicks Save Changes to write the record to disk. If the account number already exists, the program displays an error message and does not attempt to write the record. Clicking Cancel closes the internal frame without attempting to write the record.

Selecting Delete Record from the File menu displays the Delete Record internal frame in Fig. 16.19, which allows the user to delete a record from the file. The code that implements the Delete Record internal frame is in class DeleteDialog (lines 618– 774 of Fig. 16.20). The user enters the account number in the JTextField and presses Enter. Only an existing record can be deleted, so, if the specified account is empty, the program displays an error message. This enables the user to check whether the record exists before deleting the record. Clicking the Delete Record button in the internal frame sets the record’s account number to 0 (which this application considers to be an empty record). Clicking Cancel closes the internal frame without deleting the record.

Fig. 16.15 The initial Transaction Processor window.

Type account number and press the Enter key to load record.

Fig. 16.16 Loading a record into the Update Record internal frame.

946 Files and Streams

Chapter 16

Type transaction amount and press the Enter key to update balance in dialog.

Press Save Changes to store new balance in file.

Fig. 16.17 Inputting a transaction in the Update Record internal frame.

Fig. 16.18 New Record internal frame.

Fig. 16.19 Delete Record internal frame.

Chapter 16

Files and Streams

947

The TransactionProcessor program appears in Fig. 16.20. The program opens the file with file open mode "rw" (reading and writing).

1// Transaction processing program using RandomAccessFiles.

2// This program reads a random-access file sequentially,

3 // updates records already written to the file, creates new 4 // records to be placed in the file and deletes data

5 // already in the file.

6

7 // Java core packages

8import java.awt.*;

9 import java.awt.event.*;

10import java.io.*;

11import java.text.DecimalFormat;

13// Java extension packages

14import javax.swing.*;

15

16// Deitel packages

17import com.deitel.jhtp4.ch16.*;

19public class TransactionProcessor extends JFrame {

20private UpdateDialog updateDialog;

21private NewDialog newDialog;

22private DeleteDialog deleteDialog;

23private JMenuItem newItem, updateItem, deleteItem,

24openItem, exitItem;

25private JDesktopPane desktop;

26private RandomAccessFile file;

27private RandomAccessAccountRecord record;

28

29// set up GUI

30public TransactionProcessor()

31{

32super( "Transaction Processor" );

34// set up desktop, menu bar and File menu

35desktop = new JDesktopPane();

36getContentPane().add( desktop );

37

38JMenuBar menuBar = new JMenuBar();

39setJMenuBar( menuBar );

40

41JMenu fileMenu = new JMenu( "File" );

42menuBar.add( fileMenu );

43

44// set up menu item for adding a record

45newItem = new JMenuItem( "New Record" );

46newItem.setEnabled( false );

47

Fig. 16.20 Transaction-processing program (part 1 of 15).

948 Files and Streams

Chapter 16

48// display new record dialog when user selects New Record

49newItem.addActionListener(

50

 

51

new ActionListener() {

52

 

53

public void actionPerformed( ActionEvent event )

54

{

55

newDialog.setVisible( true );

56

}

57}

58);

60// set up menu item for updating a record

61updateItem = new JMenuItem( "Update Record" );

62updateItem.setEnabled( false );

63

64// display update dialog when user selects Update Record

65updateItem.addActionListener(

66

 

67

new ActionListener() {

68

 

69

public void actionPerformed( ActionEvent event )

70

{

71

updateDialog.setVisible( true );

72

}

73}

74);

76// set up menu item for deleting a record

77deleteItem = new JMenuItem( "Delete Record" );

78deleteItem.setEnabled( false );

79

80// display delete dialog when user selects Delete Record

81deleteItem.addActionListener(

82

 

83

new ActionListener() {

84

 

85

public void actionPerformed( ActionEvent event )

86

{

87

deleteDialog.setVisible( true );

88

}

89}

90);

92// set up button for opening file

93openItem = new JMenuItem( "New/Open File" );

95// enable user to select file to open, then set up

96// dialog boxes

97openItem.addActionListener(

98

99 new ActionListener() {

100

Fig. 16.20 Transaction-processing program (part 2 of 15).

Chapter 16

Files and Streams

949

 

 

 

 

101

 

public void actionPerformed( ActionEvent event )

 

102

 

{

 

103

 

boolean opened = openFile();

 

104

 

 

 

105

 

if ( !opened )

 

106

 

return;

 

107

 

 

 

108

 

openItem.setEnabled( false );

 

109

 

 

 

110

 

// set up internal frames for record processing

111

 

updateDialog = new UpdateDialog( file );

 

112

 

desktop.add( updateDialog );

 

113

 

 

 

114

 

deleteDialog = new DeleteDialog( file );

 

115

 

desktop.add ( deleteDialog );

 

116

 

 

 

117

 

newDialog = new NewDialog( file );

 

118

 

desktop.add( newDialog );

 

119

 

}

 

120

 

 

 

121

}

// end anonymous inner class

 

122

 

 

 

123

); // end call to addActionListener

 

124

 

 

 

125// set up menu item for exiting program

126exitItem = new JMenuItem( "Exit" );

127exitItem.setEnabled( true );

128

129// teminate application

130exitItem.addActionListener(

132

new ActionListener() {

133

 

134

public void actionPerformed( ActionEvent event )

135

{

136

closeFile();

137}

138}

139);

140

141// attach menu items to File menu

142fileMenu.add( openItem );

143fileMenu.add( newItem );

144fileMenu.add( updateItem );

145fileMenu.add( deleteItem );

146fileMenu.addSeparator();

147fileMenu.add( exitItem );

148

149// configure window

150setDefaultCloseOperation(

151WindowConstants.DO_NOTHING_ON_CLOSE );

153 setSize( 400, 250 );

Fig. 16.20 Transaction-processing program (part 3 of 15).

950 Files and Streams

Chapter 16

154 setVisible( true );

155

156 } // end TransactionProcessor constructor

157

158// enable user to select file to open

159private boolean openFile()

160{

161// display dialog so user can select file

162JFileChooser fileChooser = new JFileChooser();

163fileChooser.setFileSelectionMode(

164JFileChooser.FILES_ONLY );

165

166 int result = fileChooser.showOpenDialog( this );

167

168// if user clicked Cancel button on dialog, return

169if ( result == JFileChooser.CANCEL_OPTION )

170return false;

171

172// obtain selected file

173File fileName = fileChooser.getSelectedFile();

175// display error if file name invalid

176if ( fileName == null ||

177fileName.getName().equals( "" ) ) {

178JOptionPane.showMessageDialog( this,

179

"Invalid File Name", "Invalid File Name",

180

JOptionPane.ERROR_MESSAGE );

181

 

182return false;

183}

184

185 else {

186

187// open file

188try {

189

file = new RandomAccessFile( fileName, "rw" );

190

openItem.setEnabled( false );

191

newItem.setEnabled( true );

192

updateItem.setEnabled( true );

193deleteItem.setEnabled( true );

194}

195

196// process problems opening file

197catch ( IOException ioException ) {

198

JOptionPane.showMessageDialog( this,

199

"File does not exist", "Invalid File Name",

200

JOptionPane.ERROR_MESSAGE );

201

 

202return false;

203}

204}

205

206 return true; // file opened

Fig. 16.20 Transaction-processing program (part 4 of 15).

Chapter 16

Files and Streams

951

 

 

 

 

207

}

 

 

208

 

 

 

209// close file and terminate application

210private void closeFile()

211{

212// close file and exit

213try {

214if ( file != null )

215

file.close();

216

 

217System.exit( 0 );

218}

219

220// process exceptions closing file

221catch( IOException ioException ) {

222JOptionPane.showMessageDialog( this,

223

"Error closing file",

224"Error", JOptionPane.ERROR_MESSAGE );

225System.exit( 1 );

226}

227}

228

229// execute application

230public static void main( String args[] )

231{

232new TransactionProcessor();

233}

234

235 } // end class TransactionProcessor

236

237// class for udpating records

238class UpdateDialog extends JInternalFrame {

239private RandomAccessFile file;

240private BankUI userInterface;

241

242// set up GUI

243public UpdateDialog( RandomAccessFile updateFile )

244{

245super( "Update Record" );

246

247 file = updateFile;

248

249// set up GUI components

250userInterface = new BankUI( 5 );

251getContentPane().add( userInterface,

252BorderLayout.CENTER );

253

254// set up Save Changes button and register listener

255JButton saveButton = userInterface.getDoTask1Button();

256saveButton.setText( "Save Changes" );

257

258 saveButton.addActionListener(

259

Fig. 16.20 Transaction-processing program (part 5 of 15).

952

Files and Streams

Chapter 16

 

 

 

260

new ActionListener() {

 

261

 

 

262

public void actionPerformed( ActionEvent event )

263

{

 

264

addRecord( getRecord() );

 

265

setVisible( false );

 

266

userInterface.clearFields();

 

267}

268}

269);

270

271// set up Cancel button and register listener

272JButton cancelButton = userInterface.getDoTask2Button();

273cancelButton.setText( "Cancel" );

274

 

275

cancelButton.addActionListener(

276

 

277

new ActionListener() {

278

 

279

public void actionPerformed( ActionEvent event )

280

{

281

setVisible( false );

282

userInterface.clearFields();

283}

284}

285);

286

287// set up listener for transaction textfield

288JTextField transactionField =

289userInterface.getFields()[ BankUI.TRANSACTION ];

291

transactionField.addActionListener(

292

 

293

new ActionListener() {

294

 

295

public void actionPerformed( ActionEvent event )

296

{

297

// add transaction amount to balance

298

try {

299

RandomAccessAccountRecord record = getRecord();

300

 

301

// get textfield values from userInterface

302

String fieldValues[] =

303

userInterface.getFieldValues();

304

 

305

// get transaction amount

306

double change = Double.parseDouble(

307

fieldValues[ BankUI.TRANSACTION ] );

308

 

309

// specify Strings to display in GUI

310

String[] values = {

311

String.valueOf( record.getAccount() ),

312

record.getFirstName(),

 

 

Fig. 16.20

Transaction-processing program (part 6 of 15).

Chapter 16

Files and Streams

953

 

 

 

 

313

 

record.getLastName(),

 

314

 

String.valueOf( record.getBalance()

 

315

 

+ change ),

 

316

 

"Charge(+) or payment (-)" };

 

317

 

 

 

318

 

// display Strings in GUI

 

319

 

userInterface.setFieldValues( values );

 

320

 

}

 

321

 

 

 

322

 

// process invalid number in transaction field

 

323

 

catch ( NumberFormatException numberFormat ) {

 

324

 

JOptionPane.showMessageDialog( null,

 

325

 

"Invalid Transaction",

 

326

 

"Invalid Number Format",

 

327

 

JOptionPane.ERROR_MESSAGE );

 

328

 

}

 

329

 

 

 

330

 

} // end method actionPerformed

 

331

 

 

 

332

}

// end anonymous inner class

 

333

 

 

 

334

); // end call to addActionListener

 

335

 

 

 

336// set up listener for account text field

337JTextField accountField =

338userInterface.getFields()[ BankUI.ACCOUNT ];

340

accountField.addActionListener(

341

 

342

new ActionListener() {

343

 

344

// get record and display contents in GUI

345

public void actionPerformed( ActionEvent event )

346

{

347

RandomAccessAccountRecord record = getRecord();

348

 

349

if ( record.getAccount() != 0 ) {

350

String values[] = {

351

String.valueOf( record.getAccount() ),

352

record.getFirstName(),

353

record.getLastName(),

354

String.valueOf( record.getBalance() ),

355

"Charge(+) or payment (-)" };

356

 

357

userInterface.setFieldValues( values );

358

}

359

 

360

} // end method actionPerformed

361

 

362

} // end anonymous inner class

363

 

364

); // end call to addActionListener

365

 

 

 

Fig. 16.20

Transaction-processing program (part 7 of 15).

954 Files and Streams

Chapter 16

366setSize( 300, 175 );

367setVisible( false );

368}

369

370// get record from file

371private RandomAccessAccountRecord getRecord()

372{

373RandomAccessAccountRecord record =

374new RandomAccessAccountRecord();

375

376// get record from file

377try {

378JTextField accountField =

379

userInterface.getFields()[ BankUI.ACCOUNT ];

380

 

381

int accountNumber =

382

Integer.parseInt( accountField.getText() );

383

 

384

if ( accountNumber < 1 || accountNumber > 100 ) {

385

JOptionPane.showMessageDialog( this,

386

"Account Does Not Exist",

387

"Error", JOptionPane.ERROR_MESSAGE );

388return record;

389}

390

391// seek to appropriate record location in file

392file.seek( ( accountNumber - 1 ) *

393RandomAccessAccountRecord.size() );

394record.read( file );

395

 

396

if ( record.getAccount() == 0 )

397

JOptionPane.showMessageDialog( this,

398

"Account Does Not Exist",

399

"Error", JOptionPane.ERROR_MESSAGE );

400

}

401

 

402// process invalid account number format

403catch ( NumberFormatException numberFormat ) {

404JOptionPane.showMessageDialog( this,

405

"Invalid Account", "Invalid Number Format",

406JOptionPane.ERROR_MESSAGE );

407}

408

409// process file processing problems

410catch ( IOException ioException ) {

411JOptionPane.showMessageDialog( this,

412

"Error Reading File",

413"Error", JOptionPane.ERROR_MESSAGE );

414}

415

416 return record;

417

418 } // end method getRecord

Fig. 16.20 Transaction-processing program (part 8 of 15).

Chapter 16

Files and Streams

955

419

420// add record to file

421public void addRecord( RandomAccessAccountRecord record )

422{

423// update record in file

424try {

425int accountNumber = record.getAccount();

426

 

427

file.seek( ( accountNumber - 1 ) *

428

RandomAccessAccountRecord.size() );

429

 

430

String[] values = userInterface.getFieldValues();

431

 

432// set firstName, lastName and balance in record

433record.setFirstName( values[ BankUI.FIRSTNAME ] );

434record.setLastName( values[ BankUI.LASTNAME ] );

435record.setBalance(

436

Double.parseDouble( values[ BankUI.BALANCE ] ) );

437

 

438// rewrite record to file

439record.write( file );

440}

441

442// process file processing problems

443catch ( IOException ioException ) {

444JOptionPane.showMessageDialog( this,

445

"Error Writing To File",

446"Error", JOptionPane.ERROR_MESSAGE );

447}

448

449// process invalid balance value

450catch ( NumberFormatException numberFormat ) {

451JOptionPane.showMessageDialog( this,

452"Bad Balance", "Invalid Number Format",

453JOptionPane.ERROR_MESSAGE );

454}

455

456 } // end method addRecord

457

458 } // end class UpdateDialog

459

460// class for creating new records

461class NewDialog extends JInternalFrame {

462private RandomAccessFile file;

463private BankUI userInterface;

464

465// set up GUI

466public NewDialog( RandomAccessFile newFile )

467{

468super( "New Record" );

469

470 file = newFile;

471

Fig. 16.20 Transaction-processing program (part 9 of 15).

956 Files and Streams

Chapter 16

472// attach user interface to dialog

473userInterface = new BankUI( 4 );

474getContentPane().add( userInterface,

475BorderLayout.CENTER );

476

477// set up Save Changes button and register listener

478JButton saveButton = userInterface.getDoTask1Button();

479saveButton.setText( "Save Changes" );

480

 

481

saveButton.addActionListener(

482

 

483

new ActionListener() {

484

 

485

// add new record to file

486

public void actionPerformed( ActionEvent event )

487

{

488

addRecord( getRecord() );

489

setVisible( false );

490

userInterface.clearFields();

491

}

492

 

493

} // end anonymous inner class

494

 

495

); // end call to addActionListener

496

 

497JButton cancelButton = userInterface.getDoTask2Button();

498cancelButton.setText( "Cancel" );

499

 

500

cancelButton.addActionListener(

501

 

502

new ActionListener() {

503

 

504

// dismiss dialog without storing new record

505

public void actionPerformed( ActionEvent event )

506

{

507

setVisible( false );

508

userInterface.clearFields();

509

}

510

 

511

} // end anonymous inner class

512

 

513

); // end call to addActionListener

514

 

515setSize( 300, 150 );

516setVisible( false );

518 } // end constructor

519

520// get record from file

521private RandomAccessAccountRecord getRecord()

522{

523RandomAccessAccountRecord record =

524new RandomAccessAccountRecord();

Fig. 16.20 Transaction-processing program (part 10 of 15).

Chapter 16

Files and Streams

957

525

526// get record from file

527try {

528JTextField accountField =

529

userInterface.getFields()[ BankUI.ACCOUNT ];

530

 

531

int accountNumber =

532

Integer.parseInt( accountField.getText() );

533

 

534

if ( accountNumber < 1 || accountNumber > 100 ) {

535

JOptionPane.showMessageDialog( this,

536

"Account Does Not Exist",

537

"Error", JOptionPane.ERROR_MESSAGE );

538return record;

539}

540

541// seek to record location

542file.seek( ( accountNumber - 1 ) *

543

RandomAccessAccountRecord.size() );

544

 

545// read record from file

546record.read( file );

547}

548

549// process invalid account number format

550catch ( NumberFormatException numberFormat ) {

551JOptionPane.showMessageDialog( this,

552

"Account Does Not Exist", "Invalid Number Format",

553JOptionPane.ERROR_MESSAGE );

554}

555

556// process file processing problems

557catch ( IOException ioException ) {

558JOptionPane.showMessageDialog( this,

559

"Error Reading File",

560"Error", JOptionPane.ERROR_MESSAGE );

561}

562

563 return record;

564

565 } // end method getRecord

566

567// add record to file

568public void addRecord( RandomAccessAccountRecord record )

569{

570String[] fields = userInterface.getFieldValues();

571

572if ( record.getAccount() != 0 ) {

573JOptionPane.showMessageDialog( this,

574

"Record Already Exists",

575"Error", JOptionPane.ERROR_MESSAGE );

576return;

577}

Fig. 16.20 Transaction-processing program (part 11 of 15).

958 Files and Streams

Chapter 16

578

579// output the values to the file

580try {

581

582// set account, first name, last name and balance

583// for record

584record.setAccount( Integer.parseInt(

585fields[ BankUI.ACCOUNT ] ) );

586record.setFirstName( fields[ BankUI.FIRSTNAME ] );

587record.setLastName( fields[ BankUI.LASTNAME ] );

588record.setBalance( Double.parseDouble(

589

fields[ BankUI.BALANCE ] ) );

590

 

591// seek to record location

592file.seek( ( record.getAccount() - 1 ) *

593

RandomAccessAccountRecord.size() );

594

 

595// write record

596record.write( file );

597}

598

599// process invalid account or balance format

600catch ( NumberFormatException numberFormat ) {

601JOptionPane.showMessageDialog( this,

602

"Invalid Balance", "Invalid Number Format",

603JOptionPane.ERROR_MESSAGE );

604}

605

606// process file processing problems

607catch ( IOException ioException ) {

608JOptionPane.showMessageDialog( this,

609

"Error Writing To File",

610"Error", JOptionPane.ERROR_MESSAGE );

611}

612

613 } // end method addRecord

614

615 } // end class NewDialog

616

617// class for deleting records

618class DeleteDialog extends JInternalFrame {

619private RandomAccessFile file; // file for output

620private BankUI userInterface;

621

622// set up GUI

623public DeleteDialog( RandomAccessFile deleteFile )

624{

625super( "Delete Record" );

626

627 file = deleteFile;

628

629// create BankUI with only account field

630userInterface = new BankUI( 1 );

Fig. 16.20 Transaction-processing program (part 12 of 15).

Chapter 16

Files and Streams

959

631

632getContentPane().add( userInterface,

633BorderLayout.CENTER );

634

635// set up Delete Record button and register listener

636JButton deleteButton = userInterface.getDoTask1Button();

637deleteButton.setText( "Delete Record" );

638

 

639

deleteButton.addActionListener(

640

 

641

new ActionListener() {

642

 

643

// overwrite existing record

644

public void actionPerformed( ActionEvent event )

645

{

646

addRecord( getRecord() );

647

setVisible( false );

648

userInterface.clearFields();

649

}

650

 

651

} // end anonymous inner class

652

 

653

); // end call to addActionListener

654

 

655// set up Cancel button and register listener

656JButton cancelButton = userInterface.getDoTask2Button();

657cancelButton.setText( "Cancel" );

658

 

659

cancelButton.addActionListener(

660

 

661

new ActionListener() {

662

 

663

// cancel delete operation by hiding dialog

664

public void actionPerformed( ActionEvent event )

665

{

666

setVisible( false );

667

}

668

 

669

} // end anonymous inner class

670

 

671

); // end call to addActionListener

672

 

673// set up listener for account text field

674JTextField accountField =

675userInterface.getFields()[ BankUI.ACCOUNT ];

677

accountField.addActionListener(

678

 

679

new ActionListener() {

680

 

681

public void actionPerformed( ActionEvent event )

682

{

683

RandomAccessAccountRecord record = getRecord();

 

 

Fig. 16.20

Transaction-processing program (part 13 of 15).

960

Files and Streams

Chapter 16

 

 

 

684

}

 

685

 

 

686

} // end anonymous inner class

 

687

 

 

688

); // end call to addActionListener

 

689

 

 

690setSize( 300, 100 );

691setVisible( false );

693 } // end constructor

694

695// get record from file

696private RandomAccessAccountRecord getRecord()

697{

698RandomAccessAccountRecord record =

699new RandomAccessAccountRecord();

700

701// get record from file

702try {

703JTextField accountField =

704

userInterface.getFields()[ BankUI.ACCOUNT ];

705

 

706

int accountNumber =

707

Integer.parseInt( accountField.getText() );

708

 

709

if ( accountNumber < 1 || accountNumber > 100 ) {

710

JOptionPane.showMessageDialog( this,

711

"Account Does Not Exist",

712

"Error", JOptionPane.ERROR_MESSAGE );

713return( record );

714}

715

716// seek to record location and read record

717file.seek( ( accountNumber - 1 ) *

718RandomAccessAccountRecord.size() );

719record.read( file );

720

 

721

if ( record.getAccount() == 0 )

722

JOptionPane.showMessageDialog( this,

723

"Account Does Not Exist",

724

"Error", JOptionPane.ERROR_MESSAGE );

725

}

726

 

727// process invalid account number format

728catch ( NumberFormatException numberFormat ) {

729JOptionPane.showMessageDialog( this,

730

"Account

Does Not Exist",

731

"Invalid

Number Format",

732JOptionPane.ERROR_MESSAGE );

733}

734

735// process file processing problems

736catch ( IOException ioException ) {

Fig. 16.20 Transaction-processing program (part 14 of 15).

Chapter 16

Files and Streams

961

 

 

 

737

JOptionPane.showMessageDialog( this,

 

738

"Error Reading File",

 

739"Error", JOptionPane.ERROR_MESSAGE );

740}

741

742 return record;

743

744 } // end method getRecord

745

746// add record to file

747public void addRecord( RandomAccessAccountRecord record )

748{

749if ( record.getAccount() == 0 )

750return;

751

752// delete record by setting account number to 0

753try {

754int accountNumber = record.getAccount();

755

756// seek to record position

757file.seek( ( accountNumber - 1 ) *

758

RandomAccessAccountRecord.size() );

759

 

760// set account to 0 and overwrite record

761record.setAccount( 0 );

762record.write( file );

763}

764

765// process file processing problems

766catch ( IOException ioException ) {

767JOptionPane.showMessageDialog( this,

768

"Error Writing To File",

769"Error", JOptionPane.ERROR_MESSAGE );

770}

771

772 } // end method addRecord

773

774 } // end class DeleteDialog

Fig. 16.20 Transaction-processing program (part 15 of 15).

16.12 Class File

As we stated at the beginning of this chapter, the java.io package contains an abundance of classes for processing input and output. We concentrated on the classes for processing sequential files (FileInputStream and FileOutputStream), for processing object streams (ObjectInputStream and ObjectOutputStream) and for processing ran- dom-access files (RandomAccessFile). In this section, we discuss class File, which is particularly useful for retrieving information about a file or a directory from a disk. Objects of class File do not open files or provide any file-processing capabilities.

One application of a File object is to determine whether a file exists before attempting to open the file. In Common Programming Error 16.1, we warned that opening an existing file for output by using a FileOutputStream object discards the contents

962 Files and Streams

Chapter 16

of that file without warning. If a program using a File determines that a file already exists, the program can warn that the user is about to discard the original file’s contents.

Good Programming Practice 16.2

Use a File object to determine whether a file exists before opening the file with a File- OutputStream object.

Class File provides three constructors. The constructor

public File( String name )

stores the String argument name in the object. The name can contain path information as well as a file or directory name. A file or directory’s path specifies the location of the file or directory on disk. The path includes some or all of the directories leading to the file or directory. An absolute path contains all the directories, starting with the root directory that lead to a specific file or directory. Every file or directory on a particular disk drive has the same root directory in its path. A relative path contains a subset of the directories leading to a specific file or directory. Relative paths normally start from the directory in which the application began executing.

The constructor

public File( String pathToName, String name )

uses argument pathToName (an absolute or relative path) to locate the file or directory specified by name.

The constructor

public File( File directory, String name )

uses an existing File object directory (an absolute or relative path) to locate the file or directory specified by name.

Figure 16.21 discusses some common File methods. See the Java API for other File methods.

Method

Description

 

 

boolean canRead()

Returns true if a file is readable; false otherwise.

boolean canWrite()

Returns true if a file is writable; false otherwise.

boolean exists()

Returns true if the name specified as the argument to the

 

File constructor is a file or directory in the specified path;

 

false otherwise.

boolean isFile()

Returns true if the name specified as the argument to the

 

File constructor is a file; false otherwise.

boolean isDirectory()

Returns true if the name specified as the argument to the

 

File constructor is a directory; false otherwise.

boolean isAbsolute()

Returns true if the arguments specified to the File con-

 

structor indicate an absolute path to a file or directory;

 

false otherwise.

Fig. 16.21 Some commonly used File methods (part 1 of 2).

Chapter 16

Files and Streams

963

 

 

 

Method

Description

 

 

 

 

String getAbsolutePath() Returns a String with the absolute path of the file or direc-

 

tory.

String getName()

Returns a String with the name of the file or directory.

String getPath()

Returns a String with the path of the file or directory.

String getParent()

Returns a String with the parent directory of the file or

 

directory—that is, the directory in which the file or directory

 

can be found.

long length()

Returns the length of the file in bytes. If the File object

 

represents a directory, 0 is returned.

long lastModified()

Returns a platform-dependent representation of the time at

 

which the file or directory was last modified. The value

 

returned is only useful for comparison with other values

 

returned by this method.

String[] list()

Returns an array of Strings representing the contents of a

 

directory.

Fig. 16.21 Some commonly used File methods (part 2 of 2).

Good Programming Practice 16.3

Use File method isFile to determine that a File object represents a file (not a direc- tory) before attempting to open a file.

Good Programming Practice 16.4

Before attempting to open a file for reading, use File method canRead to determine whether the file is readable.

Good Programming Practice 16.5

Before attempting to open a file for writing, use File method canWrite to determine whether the file is writable.

Figure 16.22 demonstrates class File. The FileTest application creates a GUI containing a JTextField for entering a file name or directory name and a JTextArea for displaying information about the file name or directory name input.

1// Fig. 16.22: FileTest.java

2 // Demonstrating the File class.

3

4 // Java core packages

5import java.awt.*;

6 import java.awt.event.*;

7 import java.io.*;

8

Fig. 16.22 Demonstrating class File (part 1 of 4).

964 Files and Streams

Chapter 16

9 // Java extension packages

10 import javax.swing.*;

11

12public class FileTest extends JFrame

13implements ActionListener {

14

15private JTextField enterField;

16private JTextArea outputArea;

18// set up GUI

19public FileTest()

20{

21super( "Testing class File" );

23 enterField = new JTextField(

24"Enter file or directory name here" );

25enterField.addActionListener( this );

26outputArea = new JTextArea();

27

28ScrollPane scrollPane = new ScrollPane();

29scrollPane.add( outputArea );

30

31Container container = getContentPane();

32container.add( enterField, BorderLayout.NORTH );

33container.add( scrollPane, BorderLayout.CENTER );

35setSize( 400, 400 );

36show();

37}

38

39// display information about file user specifies

40public void actionPerformed( ActionEvent actionEvent )

41{

42File name = new File( actionEvent.getActionCommand() );

44// if name exists, output information about it

45if ( name.exists() ) {

46

outputArea.setText(

47

name.getName() + " exists\n" +

48

( name.isFile() ?

49

"is a file\n" : "is not a file\n" ) +

50

( name.isDirectory() ?

51

"is a directory\n" : "is not a directory\n" ) +

52

( name.isAbsolute() ? "is absolute path\n" :

53

"is not absolute path\n" ) +

54

"Last modified: " + name.lastModified() +

55

"\nLength: " + name.length() +

56

"\nPath: " + name.getPath() +

57

"\nAbsolute path: " + name.getAbsolutePath() +

58

"\nParent: " + name.getParent() );

59

 

60

// output information if name is a file

61

if ( name.isFile() ) {

 

 

Fig. 16.22

Demonstrating class File (part 2 of 4).

Chapter 16

Files and Streams

965

 

 

 

62

 

 

63

// append contents of file to outputArea

 

64

try {

 

65

BufferedReader input = new BufferedReader(

 

66

new FileReader( name ) );

 

67

StringBuffer buffer = new StringBuffer();

 

68

String text;

 

69

outputArea.append( "\n\n" );

 

70

 

 

71

while ( ( text = input.readLine() ) != null )

 

72

buffer.append( text + "\n" );

 

73

 

 

74

outputArea.append( buffer.toString() );

 

75

}

 

76

 

 

77

// process file processing problems

 

78

catch( IOException ioException ) {

 

79

JOptionPane.showMessageDialog( this,

 

80

"FILE ERROR",

 

81

"FILE ERROR", JOptionPane.ERROR_MESSAGE );

 

82

}

 

83

}

 

84

 

 

85

// output directory listing

 

86

else if ( name.isDirectory() ) {

 

87

String directory[] = name.list();

 

88

 

 

89

outputArea.append( "\n\nDirectory contents:\n");

 

90

 

 

91

for ( int i = 0; i < directory.length; i++ )

 

92

outputArea.append( directory[ i ] + "\n" );

 

93}

94}

95

96// not file or directory, output error message

97else {

98

JOptionPane.showMessageDialog( this,

99

actionEvent.getActionCommand() + " Does Not Exist",

100"ERROR", JOptionPane.ERROR_MESSAGE );

101}

102

103 } // end method actionPerformed

104

105// execute application

106public static void main( String args[] )

107{

108FileTest application = new FileTest();

110application.setDefaultCloseOperation(

111JFrame.EXIT_ON_CLOSE );

112}

113

114 } // end class FileTest

Fig. 16.22 Demonstrating class File (part 3 of 4).

966 Files and Streams

Chapter 16

 

 

 

 

Fig. 16.22 Demonstrating class File (part 4 of 4).

The user types a file name or directory name into the text field and presses the Enter key to invoke method actionPerformed (lines 40–103), which creates a new File object (line 42) and assigns it to name. Line 45 invokes File method exists to determine whether the name input by the user exists (either as a file or as a directory) on the disk. If the name input by the user does not exist, the actionPerformed method proceeds to lines 97–101 and displays a message dialog containing the name the user typed, followed by “Does Not Exist.” Otherwise, the body of the if structure (lines 45–94) executes. The program outputs the name of the file or directory, then outputs the results of testing the File object with isFile (line 48), isDirectory (line 50) and isAbsolute (line 52). Next, the program displays the values returned by lastModified (line 54),

Chapter 16

Files and Streams

967

length (line 55), getPath (line 56), getAbsolutePath (line 57) and getParent

(line 58).

If the File object represents a file (line 61), the program reads the contents of the file and displays the contents in the JTextArea. The program uses a BufferedReader object chained to a FileReader object (lines 65–66) to open the file for reading and to read the file one line at a time with method readLine (line 71). Note that the FileReader object was initialized with the File object name (line 66).

If the File object represents a directory (line 86), the program reads the contents of the directory into the program by using File method list, then displays the directory contents in the JTextArea.

The first output demonstrates a File object associated with the jfc directory from the Java 2 Software Development Kit. The second output of this program demonstrates a File object associated with the readme.txt file from the Java 2 Software Development Kit. In both cases, we specified an absolute path on our personal computer.

Note that the \ separator character is used to separate directories and files in the path. On a UNIX workstation, the separator character would be the / character. Java processes both characters identically in a path name. So, if we specified the path

c:\java/readme.txt

which uses one of each separator character, Java still processes the file properly.

Common Programming Error 16.2

Using \ as a directory separator rather than \\ in a string literal is a logic error. A single \ indicates that the \ and the next character represent an escape sequence. To insert a \ in a string literal, you must use \\.

Good Programming Practice 16.6

When building Strings that represent path information, use File.separatorChar to obtain the local computer’s proper separator character, rather than explicitly using / or \.

SUMMARY

All data items processed by a computer are reduced to combinations of zeros and ones.

The smallest data item in a computer (a bit) can assume the value 0 or the value 1.

Digits, letters and special symbols are called characters. The set of all characters used to write programs and represent data items on a particular computer is called that computer’s character set. Every character in a computer’s character set is represented as a pattern of 1s and 0s. (Characters in Java are Unicode characters composed of 2 bytes.)

A field is a group of characters (or bytes) that conveys meaning.

A record is a group of related fields.

At least one field in a record is chosen as a record key to identify a record as belonging to a particular person or entity that is unique from all other records in the file.

Java imposes no structure on a file. Notions like “record” do not exist in Java. The programmer must structure a file appropriately to meet the requirements of an application.

A collection of programs designed to create and manage databases is called a database management system (DBMS).

Java views each file as a sequential stream of bytes.

968 Files and Streams

Chapter 16

Each file ends in some machine-dependent form of end-of-file marker.

Streams provide communication channels between programs and files, memory or other programs across a network.

Programs use classes from package java.io to perform Java file I/O. This package includes the definitions for the stream classes, such as FileInputStream, FileOutputStream,

DataInputStream and DataOutputStream.

Files are opened by instantiating objects of stream classes FileInputStream, FileOutputStream, RandomAccessFile, FileReader and FileWriter.

InputStream (a subclass of Object) and OutputStream (a subclass of Object) are abstract classes that define methods for performing input and output, respectively.

File input/output is done with FileInputStream (a subclass of InputStream) and FileOutputStream (a subclass of OutputStream).

Pipes are synchronized communication channels between threads. A pipe is established between two threads. One thread sends data to another by writing to a PipedOutputStream (a subclass of OutputStream). The target thread reads information from the pipe via a PipedInputStream (a subclass of InputStream).

A PrintStream (a subclass of FilterOutputStream) performs output to the screen (or the “standard output” as defined by your local operating system). System.out is a PrintStream

(as is System.err).

A FilterInputStream filters an InputStream; a FilterOutStream filters an OutputStream. Filtering means simply that the filter stream provides additional functionality, such as buffering, monitoring of line numbers or aggregating of data bytes into meaningful primitive- data-type units.

Reading data as raw bytes is fast but crude. Usually, programs read data as aggregates of bytes that form an int, a float, a double, and so on. To accomplish this, we use a DataInputStream (a subclass of class FilterInputStream).

Interface DataInput is implemented by class DataInputStream and class RandomAccessFile; each needs to read primitive data types from a stream.

DataInputStreams enable a program to read binary data from an InputStream.

The DataInput interface includes methods read (for byte arrays), readBoolean, readByte, readChar, readDouble, readFloat, readFully (for byte arrays), readInt, readLine, readLong, readShort, readUnsignedByte, readUnsignedShort, readUTF (for Unicode) and skipBytes.

The DataOutput interface is implemented by class DataOutputStream (a subclass of class FilterOutputStream) and class RandomAccessFile; each needs to write primitive data types to an OutputStream.

DataOutputStreams enable a program to write binary data to an OutputStream. The DataOutput interface includes methods flush, size, write (for a byte), write (for a

byte array), writeBoolean, writeByte, writeBytes, writeChar, writeChars (for Unicode Strings), writeDouble, writeFloat, writeInt, writeLong, writeShort and writeUTF.

Buffering is an I/O-performance-enhancement technique.

With a BufferedOutputStream (a subclass of class FilterOutputStream), each output statement does not necessarily result in an actual physical transfer of data to the output device. Rather, each output operation is directed to a region in memory called a buffer that is large enough to hold the data of many output operations. Then, actual output to the output device is performed

Chapter 16

Files and Streams

969

in one large physical output operation each time the buffer fills. The output operations directed to the output buffer in memory are often called logical output operations.

With a BufferedInputStream, many “logical” chunks of data from a file are read as one large physical input operation into a memory buffer. As a program requests each new chunk of data, it is taken from the buffer (this is sometimes referred to as a logical input operation). When the buffer is empty, the next physical input operation from the input device is performed to read in the next group of “logical” chunks of data. Thus, the number of physical input operations is small compared with the number of read requests issued by the program.

With a BufferedOutputStream a partially filled buffer can be forced out to the device at any time with an explicit flush.

The ObjectInput interface is similar to the DataInput interface, but includes additional methods to read Objects from InputStreams.

The ObjectOutput interface is similar to the DataOutput interface, but includes additional methods to write Objects to OutputStreams.

The ObjectInputStream and ObjectOutputStream classes implement the ObjectInput and ObjectOutput interfaces, respectively.

A PushbackInputStream is a subclass of class FilterInputStream. The application reading a PushbackInputStream reads bytes from the stream and forms aggregates consisting of several bytes. Sometimes, to determine that one aggregate is complete, the application must read the first character “past the end” of the first aggregate. Once the program has determined that the current aggregate is complete, the extra character is “pushed back” onto the stream.

PushbackInputStreams are used by programs, like compilers, that parse their inputs—that is, break them into meaningful units (such as the keywords, identifiers and operators that the Java compiler must recognize).

A RandomAccessFile (a subclass of Object) is useful for such direct-access applications as transaction-processing applications, like airline-reservations systems and point-of-sale systems.

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 might be directed to any part of the file, perhaps one widely separated from the part of the file referenced in the previous request.

Direct-access applications provide rapid access to specific data items in large files; such applications are often used while people are waiting for answers—these answers must be made available quickly, or the people might become impatient and “take their business elsewhere.”

A ByteArrayInputStream (a subclass of abstract class InputStream) performs its inputs from a byte array in memory.

A ByteArrayOutputStream (a subclass of abstract class OutputStream) outputs to a byte array in memory.

An application of byte-array input/output is data validation. A program can input an entire line at a time from the input stream into a byte array. Then, a validation routine can scrutinize the contents of the byte array and correct the data, if necessary. The program can now proceed to input from the byte array, knowing that the input data is in the proper format.

A StringBufferInputStream (a subclass of abstract class InputStream) inputs from a StringBuffer object.

A SequenceInputStream (a subclass of abstract class InputStream) enables several

InputStreams to be concatenated so that the program will see the group as one continuous InputStream. As the end of each input stream is reached, the stream is closed, and the next stream in the sequence is opened.

970 Files and Streams

Chapter 16

Class BufferedReader and class BufferedWriter enable efficient buffering for charac- ter-based streams.

Class CharArrayReader and class CharArrayWriter read and write a stream of characters to a character array.

A PushbackReader (a subclass of abstract class FilterReader) enables characters to be placed back on a character stream. A LineNumberReader (a subclass of BufferedReader) is a buffered character-stream that keeps track of line numbers (e.g., a newline, a return or a carriage-return line-feed combination).

Class FileReader (a subclass of InputStreamReader) and class FileWriter (a subclass of OutputStreamWriter) read and write characters to a file. Class PipedReader and class PipedWriter are piped-character streams. Class StringReader and StringWriter read and write characters to Strings. A PrintWriter writes characters to a stream.

Class File enables programs to obtain information about a file or directory.

Files are opened for output by creating a FileOutputStream class object. One argument is passed to the constructor—the filename. Existing files are truncated, and all data in the file is lost. Nonexistent files are created.

A program can process no files, one file or several files. Each file has a unique name and is associated with an appropriate file stream object. All file-processing methods must refer to a file with the appropriate object.

A file-position pointer indicates the position in the file from which the next input is to occur or at which the next output is to be placed.

A convenient way to implement random-access files is by using only fixed-length records. Using this technique, a program can quickly calculate the exact location of a record relative to the beginning of the file.

Data can be inserted in a random-access file without destroying other data in the file. Data can be updated or deleted without rewriting the entire file.

The RandomAccessFile class has the same capabilities for input and output as the DataInputStream and DataOutputStream classes and also supports seeking to a specific byte position in the file, with method seek.

TERMINOLOGY

absolute path

canWrite method of File class

alphabetic field

chaining stream objects

alphanumeric field

character field

binary digit

character set

bit

CharArrayReader class

buffer

CharArrayWriter class

BufferedInputStream class

close a file

BufferedOutputStream class

close method

BufferedReader class

data hierarchy

BufferedWriter class

data validation

buffering

database

byte

database management system (DBMS)

ByteArrayInputStream class

DataInput interface

ByteArrayOutputStream class

DataOutput interface

CANCEL_OPTION constant

decimal digit

canRead method of File class

direct-access applications

Chapter 16

Files and Streams

971

DIRECTORIES_ONLY constant

OutputStream class

 

directory

OutputStreamWriter class

 

end-of-file

partially filled buffer

 

end-of-file marker

persistent data

 

EndOfFileException

physical input operation

 

exists method of File class

physical output operation

 

field

pipe

 

file

PipedInputStream class

 

File class

PipedOutputStream class

 

file name

PipedReader class

 

FileInputStream class

PipedWriter class

 

FileOutputStream class

PrintStream class

 

file-position pointer

PrintWriter class

 

FileReader class

PushbackInputStream class

 

FILES_AND_DIRECTORIES constant

PushbackReader class

 

FILES_ONLY constant

r file open mode

 

FileWriter class

random-access file

 

FilterInputStream class

RandomAccessFile class

 

FilterOutputStream class

read method

 

FilterReader class

readBoolean method

 

flush

readByte method

 

getAbsolutePath method of File class

readChar method

 

getName method of File class

readDouble method

 

getParent method of File class

Reader class

 

getPath method of File class

readFloat method

 

getSelectedFile method

readFully method

 

input stream

readInt method

 

InputStream class

readLong method

 

InputStreamReader class

readObject method

 

instant-access application

readShort method

 

IOException

readUnsignedByte method

 

isAbsolute method of File class

readUnsignedShort method

 

isDirectory method of File class

record

 

isFile method of File class

record key

 

JFileChooser class

relative path

 

lastModified method of File class

root directory

 

length method of File class

rw file open mode

 

LineNumberReader class

seek method

 

list method of File class

SequenceInputStream class

 

logical input operation

sequential-access file

 

logical output operation

Serializable interface

 

memory buffer

setFileSelectionMode method

 

modal dialog

showOpenDialog method

 

numeric field

showSaveDialog method

 

ObjectInput interface

standard output

 

ObjectInputStream class

StringReader class

 

ObjectOutput interface

StringWriter class

 

ObjectOutputStream class

System.err (standard error stream)

 

open a file

System.in (standard input stream)

 

output stream

System.out (standard output stream)

 

972

Files and Streams

Chapter 16

transaction-processing systems

writeChars method

truncate an existing file

writeDouble method

Unicode character set

writeFloat method

write method

writeInt method

writeBoolean method

writeLong method

writeByte method

writeObject method

writeBytes method

Writer class

writeChar method

writeShort method

SELF-REVIEW EXERCISES

16.1Fill in the blanks in each of the following statements:

a)Ultimately, all data items processed by a computer are reduced to combinations of

 

 

 

 

and

.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

b)

The smallest data item a computer can process is called a

.

c)

A

 

 

is a group of related records.

 

 

 

 

 

 

 

 

d)

Digits, letters and special symbols are referred to as

.

 

e)

A group of related files is called a

 

 

.

 

 

 

 

f)

Method

 

 

 

 

of the file stream classes FileOutputStream, FileInput-

 

Stream, and RandomAccessFile closes a file.

 

 

 

g)

RandomAccessFile method

 

 

reads an integer from the specified stream.

h)

RandomAccessFile method

 

 

 

reads a line of text from the specified

 

stream.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

i)

RandomAccessFile method

 

 

 

sets the file-position pointer to a specific

 

location in a file for input or output.

 

 

 

 

 

 

 

 

16.2State which of the following are true and which are false. If false, explain why.

a)The programmer must explicitly create the System.in, System.out and System.err objects.

b)If the file-position pointer points to a location in a sequential file other than the beginning of the file, the file must be closed and reopened to read from the beginning of the file.

c)It is not necessary to search through all the records in a random-access file to find a specific record.

d)Records in random-access files must be of uniform length.

e)Method seek must seek relative to the beginning of a file.

16.3Assume that each of the following statements applies to the same program.

a)Write a statement that opens file "oldmast.dat" for input; use ObjectInputStream object inOldMaster chained to a FileInputStream object.

b)Write a statement that opens file "trans.dat" for input; use ObjectInputStream object inTransaction chained to a FileInputStream object.

c)Write a statement that opens file "newmast.dat" for output (and creation); use ObjectOutputStream object outNewMaster chained to a FileOutputStream.

d)Write a set of statements that read a record from the file "oldmast.dat". The record consists of integer accountNumber, string name and floating-point currentBalance; use ObjectInputStream object inOldMaster.

e)Write a set of statements that read a record from the file "trans.dat". The record consists of integer accountNumber and floating-point dollarAmount; use ObjectInputStream object inTransaction.

f)Write a set of statements that outputs a record to the file "newmast.dat". The record consists of integer accountNumber, string name and floating point currentBalance; use DataOutputStream object outNewMaster.

Chapter 16

Files and Streams

973

16.4Find the error and show how to correct it in each of the following.

a)Assume account, company and amount are declared.

ObjectOutputStream outputStream;

outputStream.writeInt( account ); outputStream.writeChars( company ); outputStream.writeDouble( amount );

b)The following statement should read a record from the file "payables.dat". The

ObjectInputStream object inPayable refers to this file, and FileInputStream object inReceivable refers to the file "receivables.dat".

account = inReceivable.readInt(); companyID = inReceivable.readLong(); amount = inReceivable.readDouble();

ANSWERS TO SELF-REVIEW EXERCISES

16.1 a) 1s, 0s. b) bit. c) file. d) characters. e) database. f) close. g) readInt. h) readLine. i) seek.

16.2a) False. These three streams are created automatically for the programmer.

b)True.

c)True.

d)False. Records in a random-access file are normally of uniform length.

e)True.

16.3a) ObjectInputStream inOldMaster;

inOldMaster = new ObjectInputStream(

new FileInputStream( "oldmast.dat" ) );

b)ObjectInputStream inTransaction; inTransaction = new ObjectInputStream(

new FileInputStream( "trans.dat" ) );

c)ObjectOutputStream outNewMaster; outNewMaster = new ObjectOutputStream(

new FileOutputStream( "newmast.dat" ) );

d)accountNumber = inOldMaster.readInt(); name = inOldMaster.readUTF(); currentBalance = inOldMaster.readDouble();

e)accountNumber = inTransaction.readInt(); dollarAmount = inTransaction.readDouble();

f)outNewMaster.writeInt( accountNumber ); outNewMaster.writeUTF( name ); outNewMaster.writeDouble( currentBalance );

16.4a) Error: The file has not been opened before the attempt is made to output data to the stream.

Correction: Create a new ObjectOutputStream object chained to a FileOutputStream object to open the file for output.

b)Error: The incorrect FileInputStream object is being used to read a record from file

"payables.dat".

Correction: Use object inPayable to refer to "payables.dat".

974 Files and Streams

Chapter 16

EXERCISES

16.5Fill in the blanks in each of the following:

a)

Computers store large amounts of data on secondary storage devices as

.

b)

A

 

is composed of several fields.

 

 

 

 

 

 

 

 

 

 

c)

A field that may contain only digits, letters and blanks is called an

 

 

 

field.

d)

To facilitate the retrieval of specific records from a file, one field in each record is chosen

 

as a

 

 

.

 

 

 

 

 

 

 

 

 

 

 

 

e)

The vast majority of information stored in computer systems is stored in

 

 

 

 

files.

f)

The standard stream objects are

 

,

 

and

 

 

.

 

 

 

16.6State which of the following are true and which are false. If false, explain why.

a)The impressive functions performed by computers essentially involve the manipulation of zeros and ones.

b)People specify programs and data items as characters; computers then manipulate and process these characters as groups of zeros and ones.

c)A person’s 5-digit zip code is an example of a numeric field.

d)A person's street address is generally considered to be an alphabetic field.

e)Data items represented in computers form a data hierarchy in which data items become larger and more complex as we progress from fields to characters to bits, etc.

f)A record key identifies a record as belonging to a particular field.

g)Companies store all their information in a single file to facilitate computer processing.

h)When a program creates a file, the file is automatically retained by the computer for future reference.

16.7Exercise 16.3 asked the reader to write a series of single statements. Actually, these statements form the core of an important type of file-processing program, namely, a file-matching program. In commercial data processing, it is common to have several files in each application system. In an accounts receivable system, for example, there is generally a master file containing detailed information about each customer, such as the customer’s name, address, telephone number, outstanding balance, credit limit, discount terms, contract arrangements and possibly a condensed history of recent purchases and cash payments.

a)As transactions occur (i.e., sales are made and cash payments arrive in the mail), they are entered into a file. At the end of each business period (i.e., a month for some companies, a week for others, and a day in some cases) the file of transactions (called "trans.dat" in Exercise 16.3) is applied to the master file (called "oldmast.dat" in Exercise 16.3), thus updating each account’s record of purchases and payments. During an updating run, the master file is rewritten as a new file ("newmast.dat"), which is then used at the end of the next business period to begin the updating process again.

b)File-matching programs must deal with certain problems that do not exist in single-file programs. For example, a match does not always occur. A customer on the master file might not have made any purchases or cash payments in the current business period; therefore, no record for this customer will appear on the transaction file. Similarly, a customer who did make some purchases or cash payments could have just moved to this community, and the company might not have had a chance to create a master record for this customer.

c)Use the statements in Exercise 16.3 as a basis for writing a complete file-matching accounts receivable program. Use the account number on each file as the record key for matching purposes. Assume that each file is a sequential file with records stored in increasing account-number order.

d)When a match occurs (i.e., records with the same account number appear on both the master file and the transaction file), add the dollar amount on the transaction file to the

Chapter 16

Files and Streams

975

current balance on the master file, and write the "newmast.dat" record. (Assume that purchases are indicated by positive amounts on the transaction file, payments by negative amounts.) When there is a master record for a particular account but no corresponding transaction record, merely write the master record to "newmast.dat". When there is a transaction record but no corresponding master record, print the message "Unmatched transaction record for account number …" (fill in the account number from the transaction record).

16.8After writing the program of Exercise 16.7, write a simple program to create some test data for checking out the program. Use the sample account data in Fig. 16.23 and Fig. 16.24. Run the program of Exercise 16.7, using the files of test data created in this exercise. Print the new master file. Check that the accounts have been updated correctly.

16.9It is possible (actually common) to have several transaction records with the same record key. This occurs because a particular customer might make several purchases and cash payments during a business period. Rewrite your accounts receivable file-matching program of Exercise 16.7 to provide for the possibility of handling several transaction records with the same record key. Modify the test data of Exercise 16.8 to include the additional transaction records in Fig. 16.25.

Master file

 

 

Account number

Name

Balance

 

 

 

100

Alan Jones

348.17

300

Mary Smith

27.19

500

Sam Sharp

0.00

700

Suzy Green

-14.22

Fig. 16.23 Sample data for master file.

Transaction file

 

Account number

Transaction amount

 

 

100

27.14

300

62.11

400

100.56

900

82.17

Fig. 16.24 Sample data for transaction file.

Account number

Dollar amount

 

 

300

83.89

700

80.78

700

1.53

Fig. 16.25 Additional transaction records.

976 Files and Streams

Chapter 16

16.10You are the owner of a hardware store and need to keep an inventory that can tell you what different tools you have, how many of each you have on hand and the cost of each one. Write a program that initializes the random-access file "hardware.dat" to one hundred empty records, lets you input the data concerning each tool, enables you to list all your tools, lets you delete a record for a tool that you no longer have and lets you update any information in the file. The tool identification number should be the record number. Use the information in Fig. 16.26 to start your file.

16.11(Telephone Number Word Generator) Standard telephone keypads contain the digits 0 through 9. The numbers 2 through 9 each have three letters associated with them (see Fig. 16.27).

Many people find it difficult to memorize phone numbers, so they use the correspondence between digits and letters to develop seven-letter words that correspond to their phone numbers. For example, a person whose telephone number is 686-2377 might use the correspondence indicated in Fig. 16.27 to develop the seven-letter word “NUMBERS.” Each seven-letter word corresponds to exactly one seven-digit telephone number. The restaurant wishing to increase its takeout business could surely do so with the number 825-3688 (i.e., “TAKEOUT”).

Record #

Tool name

Quantity

Cost

 

 

 

 

3

Electric sander

18

35.99

19

Hammer

128

10.00

26

Jig saw

16

14.25

39

Lawn mower

10

79.50

56

Power saw

8

89.99

76

Screwdriver

236

4.99

81

Sledge hammer

32

19.75

88

Wrench

65

6.48

 

 

 

 

Fig. 16.26

Data for Exercise 16.10.

 

 

Digit

Letters

 

 

2

A B C

3

D E F

4

G H I

5

J K L

6

M N O

7

P R S

8

T U V

9

W X Y

Fig. 16.27 Telephone keypad digits and letters.

Chapter 16

Files and Streams

977

Each seven-letter phone number corresponds to many separate seven-letter words. Unfortunately, most of these represent unrecognizable juxtapositions of letters. It is possible, however, that the owner of a barber shop would be pleased to know that the shop’s telephone number, 424-7288, corresponds to “HAIRCUT.” The owner of a liquor store would, no doubt, be delighted to find that the store’s number, 233-7226, corresponds to “BEERCAN.” A veterinarian with the phone number 738-2273 would be pleased to know that the number corresponds to the letters “PETCARE.” An automotive dealership would be pleased to know that the dealership number, 639-2277, corresponds to “NEWCARS.”

Write a program that, given a seven-digit number, writes to a file every possible seven-letter

word combination corresponding to that number. There are 2187 (37) such combinations. Avoid phone numbers with the digits 0 and 1.