- •Contents
- •Preface
- •Introduction to Computers, the Internet and the Web
- •1.3 Computer Organization
- •Languages
- •1.9 Java Class Libraries
- •1.12 The Internet and the World Wide Web
- •1.14 General Notes about Java and This Book
- •Sections
- •Introduction to Java Applications
- •2.4 Displaying Text in a Dialog Box
- •2.5 Another Java Application: Adding Integers
- •2.8 Decision Making: Equality and Relational Operators
- •Introduction to Java Applets
- •3.2 Sample Applets from the Java 2 Software Development Kit
- •3.3 A Simple Java Applet: Drawing a String
- •3.4 Two More Simple Applets: Drawing Strings and Lines
- •3.6 Viewing Applets in a Web Browser
- •3.7 Java Applet Internet and World Wide Web Resources
- •Repetition)
- •Class Attributes
- •5.8 Labeled break and continue Statements
- •5.9 Logical Operators
- •Methods
- •6.2 Program Modules in Java
- •6.7 Java API Packages
- •6.13 Example Using Recursion: The Fibonacci Series
- •6.16 Methods of Class JApplet
- •Class Operations
- •Arrays
- •7.6 Passing Arrays to Methods
- •7.8 Searching Arrays: Linear Search and Binary Search
- •Collaboration Among Objects
- •8.2 Implementing a Time Abstract Data Type with a Class
- •8.3 Class Scope
- •8.4 Controlling Access to Members
- •8.5 Creating Packages
- •8.7 Using Overloaded Constructors
- •8.9 Software Reusability
- •8.10 Final Instance Variables
- •Classes
- •8.16 Data Abstraction and Encapsulation
- •9.2 Superclasses and Subclasses
- •9.5 Constructors and Finalizers in Subclasses
- •Conversion
- •9.11 Type Fields and switch Statements
- •9.14 Abstract Superclasses and Concrete Classes
- •9.17 New Classes and Dynamic Binding
- •9.18 Case Study: Inheriting Interface and Implementation
- •9.19 Case Study: Creating and Using Interfaces
- •9.21 Notes on Inner Class Definitions
- •Strings and Characters
- •10.2 Fundamentals of Characters and Strings
- •10.21 Card Shuffling and Dealing Simulation
- •Handling
- •Graphics and Java2D
- •11.2 Graphics Contexts and Graphics Objects
- •11.5 Drawing Lines, Rectangles and Ovals
- •11.9 Java2D Shapes
- •12.12 Adapter Classes
- •Cases
- •13.3 Creating a Customized Subclass of JPanel
- •Applications
- •Controller
- •Exception Handling
- •14.6 Throwing an Exception
- •14.7 Catching an Exception
- •Multithreading
- •15.3 Thread States: Life Cycle of a Thread
- •15.4 Thread Priorities and Thread Scheduling
- •15.5 Thread Synchronization
- •15.9 Daemon Threads
- •Multithreading
- •Design Patterns
- •Files and Streams
- •16.2 Data Hierarchy
- •16.3 Files and Streams
- •Networking
- •17.2 Manipulating URIs
- •17.3 Reading a File on a Web Server
- •17.4 Establishing a Simple Server Using Stream Sockets
- •17.5 Establishing a Simple Client Using Stream Sockets
- •17.9 Security and the Network
- •18.2 Loading, Displaying and Scaling Images
- •18.3 Animating a Series of Images
- •18.5 Image Maps
- •18.6 Loading and Playing Audio Clips
- •18.7 Internet and World Wide Web Resources
- •Data Structures
- •19.4 Linked Lists
- •20.8 Bit Manipulation and the Bitwise Operators
- •Collections
- •21.8 Maps
- •21.9 Synchronization Wrappers
- •21.10 Unmodifiable Wrappers
- •22.2 Playing Media
- •22.3 Formatting and Saving Captured Media
- •22.5 Java Sound
- •22.8 Internet and World Wide Web Resources
- •Hexadecimal Numbers
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.