Working with Files in JAVA

Tarun Arora

Computer programs are only useful if they interact with the rest of the world in some way. This interaction is referred to as input/output , or I/O. Up until now, this article has concentrated on just one type of interaction: interaction with the user, through either a graphical user interface or a command-line interface. But the user is only one possible source of information and only one possible destination for information. We have already encountered one other type of input/output, since TextIO can read data from files and write data to files. However, Java has an input/output framework that provides much more power and flexibility than does TextIO, and that covers other kinds of I/O in addition to files. Most importantly, it supports communication over network connections. In Java, input/output involving files and networks is based on streams, which are objects that support I/O commands that are similar to those that you have already used. In fact, standard output (System.out) and standard input (System.in) are examples of streams.

Working with files requires familiarity with exceptions. Many of the subroutines that are used can throw exceptions that require mandatory exception handling. This generally means calling the subroutine in a try..catch statement that can deal with the exception if one occurs.

Streams, Readers, and Writers

Without the ability to interact with the rest of the world, a program would be useless. The interaction of a program with the rest of the world is referred to as input/output or I/O. Historically, one of the hardest parts of programming language design has been coming up with good facilities for doing input and output. A computer can be connected to many different types of input and output devices. If a programming language had to deal with each type of device as a special case, the complexity would be overwhelming. One of the major achievements in the history of programming has been to come up with good abstractions for representing I/O devices. In Java, the main I/O abstractions are called streams. Other I/O abstractions, such as “files” and “channels” also exist, but in this section we will look only at streams. Every stream represents either a source of input or a destination to which output can be sent.

Files

The data and programs in a computer’s main memory survive only as long as the power is on. For more permanent storage, computers use files, which are collections of data stored on a hard disk, on a USB memory stick, on a CD-ROM, or on some other type of storage device. Files are organized into directories (sometimes called folders). A directory can hold other directories, as well as files. Both directories and files have names that are used to identify them. Programs can read data from existing files. They can create new files and can write data to files. In Java, such input and output can be done using streams. Human-readable character data is read from a file using an object belonging to the class FileReader, which is a subclass of Reader. Similarly, data is written to a file in human-readable format through an object of type FileWriter, a subclass of Writer. For files that store data in machine format, the appropriate I/O classes are FileInputStream and FileOutputStream. In this article, we will only discuss character-oriented file I/O using the FileReader and FileWriter classes. However, FileInputStream and FileOutputStream are used in an exactly parallel fashion. All these classes are defined in the java.io package.

It’s worth noting right at the start that applets which are downloaded over a network connection are not allowed to access files (unless you have made a very foolish change to your web browser’s configuration). This is a security consideration. You can download and run an applet just by visiting a Web page with your browser. If downloaded applets had access to the files on your computer, it would be easy to write an applet that would destroy all the data on a computer that downloads it. To prevent such possibilities, there are a number of things that downloaded applets are not allowed to do. Accessing files is one of those forbidden things. Standalone programs written in Java, however, have the same access to your files as any other program. When you write a standalone Java application, you can use all the file operations described in this section.

Reading and Writing Files

The FileReader class has a constructor which takes the name of a file as a parameter and creates an input stream that can be used for reading from that file. This constructor will throw an exception of type FileNotFoundException if the file doesn’t exist. It requires mandatory exception handling, so you have to call the constructor in a try..catch statement (or inside aroutine that is declared to throw the exception). For example, suppose you have a file named “data.txt”, and you want your program to read data from that file. You could do the following to create an input stream for the file:

FileReader data; // (Declare the variable before the
// try statement, or else the variable
// is local to the try block and you won’t
// be able to use it later in the program.)

try {
data = new FileReader("data.txt"); // create the stream
}
catch (FileNotFoundException e) {
... // do something to handle the error---maybe, end the program
}

The FileNotFoundException class is a subclass of IOException, so it would be acceptable to catch IOExceptions in the above try...catch statement. More generally, just about any error that can occur during input/output operations can be caught by a catch clause that handles IOException.

Once you have successfully created a FileReader, you can start reading data from it. But since FileReaders have only the primitive input methods inherited from the basic Reader class, you will probably want to wrap your FileReader in a Scanner, in a TextReader, or in some other wrapper class. To create a TextReader for reading from a file named data.dat, you could say:

TextReader data;
try {
data = new TextReader( new FileReader("data.dat") );
}
catch (FileNotFoundException e) {
... // handle the exception
}

Once you have a TextReader named data, you can read from it using such methods as data.getInt() and data.peek(), exactly as you would from any other TextReader. Working with output files is no more difficult than this. You simply create an object
belonging to the class FileWriter. You will probably want to wrap this output stream in an object of type PrintWriter. For example, suppose you want to write data to a file named “result.dat”.

Since the constructor for FileWriter can throw an exception of type IOException, you should use a try..catch statement:

PrintWriter result;
try {
result = new PrintWriter(new FileWriter("result.dat"));
}
catch (IOException e) {
... // handle the exception
}

If no file named result.dat exists, a new file will be created. If the file already exists, then the current contents of the file will be erased and replaced with the data that your program writes to the file. This will be done without any warning. To avoid overwriting a file that already exists, you can check whether a file of the same name already exists before trying to create the stream, as discussed later in this article. An IOException might occur in the PrintWriter constructor if, for example, you are trying to create a file on a disk that is “write-protected,” meaning that it cannot be modified.

After you are finished using a file, it’s a good idea to close the file, to tell the operating system that you are finished using it. You can close a file by calling the close() method of the associated stream. Once a file has been closed, it is no longer possible to read data from it or write data to it, unless you open it again as a new stream. (Note that for most stream classes, the close() method can throw an IOException, which must be handled; however, both PrintWriter and TextReader override this method so that it cannot throw such exceptions.) If you forget to close a file, the file will ordinarily be closed automatically when the program terminates or when the file object is garbage collected, but in the case of an output file, some of the data that has been written to the file might be lost. This can occur because data that is written to a file can be buffered; that is, the data is not sent immediately to the file but is retained in main memory (in a “buffer”) until a larger chunk of data is ready to be written.

This is done for efficiency. The close() method of an output stream will cause all the data in the buffer to be sent to the file. Every output stream also has a flush() method that can be called to force any data in the buffer to be written to the file without closing the file.
As a complete example, here is a program that will read numbers from a file named data.dat, and will then write out the same numbers in reverse order to another file named result.dat. It is assumed that data.dat contains only one number on each line. Exception-handling is used to check for problems along the way. Although the application is not a particularly useful one, this program demonstrates the basics of working with files.

import java.io.*;
import java.util.ArrayList;
/**
* Reads numbers from a file named data.dat and writes them to a file * named result.dat in reverse order. The input file should contain * exactly one real number per line. */
public class ReverseFile { public static void main(String[] args) { TextReader data; // Character input stream for reading data. PrintWriter result; // Character output stream for writing data. ArrayList<Double> numbers; // An ArrayList for holding the data. numbers = new ArrayList<Double>(); try { // Create the input stream. data = new TextReader(new FileReader("data.dat")); }
catch (FileNotFoundException e) { System.out.println("Can’t find file data.dat!"); return; // End the program by returning from main(). }
try { // Create the output stream. result = new PrintWriter(new FileWriter("result.dat")); }
catch (IOException e) { System.out.println("Can’t open file result.dat!"); System.out.println("Error: " + e); data.close(); // Close the input file. return; // End the program. }

 

try { // Read numbers from the input file, adding them to the ArrayList. while ( data.eof() == false ) { // Read until end-of-file. double inputNumber = data.getlnDouble(); numbers.add( inputNumber ); }
// Output the numbers in reverse order. for (int i = numbers.size()-1; i >= 0; i--) result.println(numbers.get(i)); System.out.println("Done!"); }
catch (IOException e) { // Some problem reading the data from the input file.
System.out.println("Input Error: " + e.getMessage());
}
finally {
// Finish by closing the files, whatever else may have happened.
data.close();
result.close();
}
} // end of main()
} // end of class

Files and Directories

The subject of file names is actually more complicated. To fully specify a file, you have to give both the name of the file and the name of the directory where that file is located. A simple file name like “data.dat” or “result.dat” is taken to refer to a file in a directory that is called the current directory (also known as the “default directory” or “working directory”). The current directory is not a permanent thing. It can be changed by the user or by a program. Files not in the current directory must be referred to by a path name, which includes both the name of the file and information about the directory where it can be found.

To complicate matters even further, there are two types of path names, absolute path names and relative path names. An absolute path name uniquely identifies one file among all the files available to the computer. It contains full information about which directory the file is in and what the file’s name is. A relative path name tells the computer how to locate the file starting from the current directory.

Unfortunately, the syntax for file names and path names varies somewhat from one type of computer to another. Here are some examples:

data.dat — on any computer, this would be a file named “data.dat” in the current directory.
/home/eck/java/examples/data.dat — This is an absolute path name in a UNIX op-
erating system, including Linux and Mac OS X. It refers to a file named data.dat in a directory named examples, which is in turn in a directory named java, . . . .

C:\eck\java\examples\data.dat — An absolute path name on a Windows computer. Hard Drive:java:examples:data.dat — Assuming that “Hard Drive” is the name of a disk drive, this would be an absolute path name on a computer using a classic Macintosh operating system such as Mac OS 9.

examples/data.dat — a relative path name under UNIX. “Examples” is the name of a directory that is contained within the current directory, and data.data is a file in that directory. The corresponding relative path name for Windows would be examples\data.dat.

../examples/data.dat— a relative path name in UNIX that means “go to the directory that contains the current directory, then go into a directory named examples inside that directory, and look there for a file named data.data.” In general, “..” means “go up one
directory.”

It’s reasonably safe to say, though, that if you stick to using simple file names only, and if the files are stored in the same directory with the program that will use them, then you will be OK. It is possible for a Java program to find out the absolute path names for two important directories, the current directory and the user’s home directory. The names of these directories are system properties, and they can be read using the function calls:

System.getProperty("user.dir") — returns the absolute path name of the current directory as a String.
System.getProperty("user.home")—returns the absolute path name of the user’s home directory as a String.

To avoid some of the problems caused by differences in path names between platforms, Java has the class java.io.File. An object belonging to this class represents a file. More precisely, an object of type File represents a file name rather than a file as such. The file to which the name refers might or might not exist. Directories are treated in the same way as files, so a File object can represent a directory just as easily as it can represent a file.

A File object has a constructor, new File(String), that creates a File object from a path
name. The name can be a simple name, a relative path, or an absolute path. For example, new File("data.dat") creates a File object that refers to a file named data.dat, in the current directory. Another constructor, new File(File,String), has two parameters. The first is a File object that refers to the directory that contains the file. The second can be the name of the file or a relative path from the directory to the file.

File objects contain several useful instance methods. Assuming that file is a variable of type File, here are some of the methods that are available:

file.exists() — This boolean-valued function returns true if the file named by the File object already exists. You can use this method if you want to avoid overwriting the contents of an existing file when you create a new FileWriter.

file.isDirectory() — This boolean-valued function returns true if the File object refers to a directory. It returns false if it refers to a regular file or if no file with the given name exists.

file.delete() — Deletes the file, if it exists. Returns a boolean value to indicate whether the file was successfully deleted.

file.list() — If the File object refers to a directory, this function returns an array of type String[] containing the names of the files in that directory. Otherwise, it returns null.

Here, for example, is a program that will list the names of all the files in a directory specified by the user. Just for fun, I have used a Scanner to read the user’s input:

import java.io.File;
import  java.util.Scanner;
/**
* This program lists the files in a directory specified by * the user. The user is asked to type in a directory name. * If the name entered by the user is not a directory, a * message is printed and the program ends. */
public class DirectoryList {
public static void main(String[] args) { String directoryName; // Directory name entered by the user. File directory; // File object referring to the directory. String[] files; // Array of file names in the directory. Scanner scanner; // For reading a line of input from the user. scanner = new Scanner(System.in); // scanner reads from standard input.
System.out.print("Enter a directory name: ");
directoryName = scanner.nextLine().trim(); directory = new File(directoryName);
if (directory.isDirectory() == false) { if (directory.exists() == false) System.out.println("There is no such directory!"); else System.out.println("That file is not a directory."); }
else { files = directory.list();
System.out.println("Files in directory \"" + directory + "\":");
for (int i = 0; i < files.length; i++)
System.out.println(" " + files[i]); } } // end main() } // end class DirectoryList

All the classes that are used for reading data from files and writing data to files have constructors that take a File object as a parameter. For example, if file is a variable of type File, and you want to read character data from that file, you can create a FileReader to do so by saying new FileReader(file). If you want to use a TextReader to read from the file, you could say:

TextReader data;
try {
data = new TextReader( new FileReader(file) );
}
catch (FileNotFoundException e) {
... // handle the exception
}

File Dialog Boxes

In many programs, you want the user to be able to select the file that is going to be used for input or output. If your program lets the user type in the file name, you will just have to assume that the user understands how to work with files and directories. But in a graphical user interface, the user expects to be able to select files using a file dialog box, which is a window that a program can open when it wants the user to select a file for input or output. Swing includes a platform-independent technique for using file dialog boxes in the form of a class called JFileChooser. This class is part of the package javax.swing. File dialog boxes are similar to those, but are a little more complicated to use.

A file dialog box shows the user a list of files and sub-directories in some directory, and makes it easy for the user to specify a file in that directory. The user can also navigate easily from one directory to another. The most common constructor for JFileChooser has no parameter and sets the starting directory in the dialog box to be the user’s home directory. There are also constructors that specify the starting directory explicitly: 

new JFileChooser( File startDirectory )
new JFileChooser( String pathToStartDirectory )

Constructing a JFileChooser object does not make the dialog box appear on the screen. You have to call a method in the object to do that. There are two different methods that can be used because there are two types of file dialog: An open file dialog allows the user to specify an existing file to be opened for reading data into the program; a save file dialog lets the user specify a file, which might or might not already exist, to be opened for writing data from the program. File dialogs of these two types are opened using the showOpenDialog and showSaveDialog methods. These methods make the dialog box appear on the screen; the methods do not end until the user selects a file or cancels the dialog.

A file dialog box always has a parent, another component which is associated with the dialog box. The parent is specified as a parameter to the showOpenDialog or showSaveDialog methods. The parent is a GUI component, and can often be specified as “this” in practice, since file dialogs are often used in instance methods of GUI component classes. (The parameter can also be null, in which case an invisible component is created to be used as the parent.) Both showOpenDialog and showSaveDialog have a return value, which will be one of the constants JFileChooser.CANCEL_OPTION, JFileChooser.ERROR_OPTION, or JFileChooser.APPROVE_OPTION. If the return value is JFileChooser.APPROVE_OPTION, then the user has selected a file. If the return value is something else, then the user did not select a file. The user might have clicked a “Cancel” button, for example. You should always check the return value, to make sure that the user has, in fact, selected a file. If that is the case, then you can find out which file was selected by calling the JFileChooser ’s getSelectedFile() method, which returns an object of type File that represents the selected file. 

Putting all this together, we can look at a typical subroutine that reads data from a file that is selected using a JFileChooser :

 public void readFile() {
if (fileDialog == null) // (fileDialog is an instance variable) fileDialog = new JFileChooser(); fileDialog.setDialogTitle("Select File for Reading"); fileDialog.setSelectedFile(null); // No file is initially selected.
int option = fileDialog.showOpenDialog(this); // (Using "this" as a parameter to showOpenDialog() assumes that the // readFile() method is an instance method in a GUI component class.)
if (option != JFileChooser.APPROVE_OPTION) return; // User canceled or clicked the dialog’s close box. File selectedFile = fileDialog.getSelectedFile(); TextReader in; // (or use some other wrapper class) try {
FileReader stream = new FileReader(selectedFile); // (or a FileInputStream)
in = new TextReader( stream ); }
catch (Exception e) { JOptionPane.showMessageDialog(this, "Sorry, but an error occurred while trying to open the file:\n" + e);
return;
}
try { .
. // Read and process the data from the input stream, in. .
in.close(); }
catch (Exception e) { JOptionPane.showMessageDialog(this, "Sorry, but an error occurred while trying to read the data:\n" + e); } }

One fine point here is that the variable fileDialog is an instance variable of type JFileChoser. This allows the file dialog to continue to exist between calls to readFile(). The main effect of this is that the dialog box will keep the same selected directory from one call of readFile() to the next. When the dialog reappears, it will show the same directory that the user selected the previous time it appeared. This is probably what the user expects.

Note that it’s common to do some configuration of a JFileChooser before calling showOpenDialog or showSaveDialog. For example, the instance method setDialogTitle(String) is used to specify a title to appear in the title bar of the window.
And setSelectedFile(File) is used to set the file that is selected in the dialog box when it appears. This can be used to provide a default file choice for the user. In the readFile() method, above, fileDialog.setSelectedFile(null) specifies that no file is pre-selected when the dialog box appears.

Writing data to a file is similar, but it’s a good idea to add a check to determine whether the output file that is selected by the user already exists. In that case, ask the user whether to replace the file. Here is a typical subroutine for writing to a user-selected file:

public void writeFile() {
if (fileDialog == null) fileDialog = new JFileChooser(); // (fileDialog is an instance variable) File selectedFile = new File("(default file name)"); fileDialog.setSelectedFile(selectedFile); // Specify a default file name. fileDialog.setDialogTitle("Select File for Writing"); int option = fileDialog.showSaveDialog(this); if (option != JFileChooser.APPROVE_OPTION) return; // User canceled or clicked the dialog’s close box. selectedFile = fileDialog.getSelectedFile();
if (selectedFile.exists()) { // Ask the user whether to replace the file.
int response = JOptionPane.showConfirmDialog( this,
"The file \"" + selectedFile.getName()
+ "\" already exists.\nDo you want to replace it?", "Confirm Save",
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE );
if (response == JOptionPane.NO OPTION) return; // User does not want to replace the file. }
PrintWriter out; // (or use some other wrapper class) try {
FileWriter stream = new FileWriter(selectedFile); // (or FileOutputStream)
out = new PrintWriter( stream );
}
catch (Exception e) { JOptionPane.showMessageDialog(this, "Sorry, but an error occurred while trying to open the file:\n" + e);
return;
}
try {
.
. // Write data to the output stream, out.
.
out.close(); if (out.checkError()) // (need to check for errors in PrintWriter)
throw new IOException("Error check failed.");
}
catch (Exception e) {
JOptionPane.showMessageDialog(this,
"Sorry, but an error occurred while trying to write the data:\n" + e);
}
}

The readFile() and writeFile() routines presented here can be used, with just a few

changes, when you need to read or write a file in a GUI program.

As a first example, we look at a simple command-line program that can make a copy of a file. Copying a file is a pretty common operation, and every operating system already has a command for doing it. However, it is still instructive to look at a Java program that does the same thing. Many file operations are similar to copying a file, except that the data from the input file is processed in some way before it is written to the output file. All such operations can be done by programs with the same general form.

Since the program should be able to copy any file, we can’t assume that the data in the file is in human-readable form. So, we have to use InputStream and OutputStream to operate on the file rather than Reader and Writer. The program simply copies all the data from the InputStream to the OutputStream, one byte at a time. If source is the variable that refers to the InputStream, then the function source.read() can be used to read one byte. This function returns the value -1 when all the bytes in the input file have been read. Similarly, if copy refers to the OutputStream, then copy.write(b) writes one byte to the output file. So, the heart of the program is a simple while loop. As usual, the I/O operations can throw exceptions, so this must be done in a try..catch statement:

while(true) {
int data = source.read();
if (data < 0)
break;
copy.write(data);
}

The file-copy command in an operating system such as UNIX uses command line arguments to specify the names of the files. For example, the user might say “copy original.dat backup.dat” to copy an existing file, original.dat, to a file named backup.dat. Command line arguments can also be used in Java programs. The command line arguments are stored in the array of strings, args, which is a parameter to the main() routine. The program can retrieve the command-line arguments from this array. For example, if the program is named CopyFile and if the user runs the program with the command “java CopyFile work.dat oldwork.dat”, then in the program, args[0] will be the string "work.dat" and args[1] will be the string "oldwork.dat". The value of args.length tells the program how many command-line arguments were specified by the user.

My CopyFile program gets the names of the files from the command-line arguments. It prints an error message and exits if the file names are not specified. To add a little interest, there are two ways to use the program. The command line can simply specify the two file names. In that case, if the output file already exists, the program will print an error message and end.

This is to make sure that the user won’t accidentally overwrite an important file. However, if the command line has three arguments, then the first argument must be “-f” while the second and third arguments are file names. The -f is a command-line option, which is meant to modify the behavior of the program. The program interprets the -f to mean that it’s OK to overwrite an existing program. (The “f” stands for “force,” since it forces the file to be copied in spite of what would otherwise have been considered an error.) You can see in the source code how the command line arguments are interpreted by the program:

import  java.io.*;
/**
* Makes a copy of a file. The original file and the name of the copy must be given as
* command-line arguments. In addition, the first command-line
* argument can be "-f"; if present, the program
* will overwrite an existing file; if not, the program will report
* an error and end if the output file already exists. The number of bytes
* that are copied is reported.
*/

public class CopyFile { public static void main(String[] args) { String sourceName; // Name of the source file, // as specified on the command line.
String copyName; // Name of the copy,
// as specified on the command line. InputStream source; // Stream for reading from the source file. OutputStream copy; // Stream for writing the copy. boolean force; // This is set to true if the "-f" option // is specified on the command line.

int byteCount; // Number of bytes copied from the source file.
/* Get file names from the command line and check for the presence of the -f option. If the command line is not one of the two possible legal forms, print an error message and end this program. */
if (args.length == 3 && args[0].equalsIgnoreCase("-f")) { sourceName = args[1]; copyName = args[2]; force = true; }
else if (args.length == 2) { sourceName = args[0]; copyName = args[1]; force = false; }
else { System.out.println( "Usage: java CopyFile <source-file> <copy-name>"); System.out.println(
" or java CopyFile -f <source-file> <copy-name>"); return; }

/* Create the input stream. If an error occurs, end the program. */

try { source = new FileInputStream(sourceName); }
catch (FileNotFoundException e) {
System.out.println("Can’t find file \"" + sourceName + "\"."); return; }
/* If the output file already exists and the -f option was not specified, print an error message and end the program. */
File file = new File(copyName);

 

if (file.exists() && force == false) { System.out.println( "Output file exists. Use the -f option to replace it."); return; }
/* Create the output stream. If an error occurs, end the program. */
try { copy = new FileOutputStream(copyName); }
catch (IOException e) { System.out.println("Can’t open output file \"" + copyName + "\"."); return;
}

/* Copy one byte at a time from the input stream to the output stream, ending when the read() method returns -1 (which is the signal that the end of the stream has been reached). If any
error occurs, print an error message. Also print a message if the file has been copied successfully. */
byteCount = 0; try { while (true) { int data = source.read(); if (data < 0) break; copy.write(data); byteCount++; }
source.close(); copy.close(); System.out.println("Successfully copied " + byteCount + " bytes."); }
catch (Exception e) { System.out.println("Error occurred while copying. " + byteCount + " bytes copied."); System.out.println("Error: " + e); } } // end main() } // end class CopyFile

Persistent Data

Once a program ends, any data that was stored in variables and objects in the program is gone. In many cases, it would be useful to have some of that data stick around so that it will be available when the program is run again. The problem is, how to make the data persistent between runs of the program? The answer, of course, is to store the data in a file (or, for some applications, in a database—but the data in a database is itself stored in files).

Consider a “phone book” program that allows the user to keep track of a list of names and associated phone numbers. The program would make no sense at all if the user had to create the whole list from scratch each time the program is run. It would make more sense to think of the phone book as a persistent collection of data, and to think of the program as an interface to
that collection of data. The program would allow the user to look up names in the phone book and to add new entries. Any changes that are made should be preserved after the program ends.

The sample program PhoneDirectoryFileDemo.java is a very simple implementation of this idea. It is meant only as an example of file use; the phone book that it implements is a “toy” version that is not meant to taken seriously. This program stores the phone book data in a file named “.phone book demo” in the user’s home directory. To find the user’s home directory, it uses the System.getProperty() method. When the program starts, it checks whether the file already exists. If it does, it should contain the user’s phone book, which was saved in a previous run of the program, so the data from the file is read and entered into a TreeMap named phoneBook that represents the phone book while the program is running.  In order to store the phone book in a file,
some decision must be made about how the data in the phone book will be represented. For this example, I chose a simple representation in which each line of the file contains one entry consisting of a name and the associated phone number. A percent sign (’%’) separates the name from the number. The following code at the beginning of the program will read the phone book data file, if it exists and has the correct format: 

File  userHomeDirectory = new File( System.getProperty("user.home") );
File  dataFile = new File( userHomeDirectory, ".phone book data" );
if ( ! dataFile.exists() ) { System.out.println("No phone book data file found."); System.out.println("A new one will be created."); System.out.println("File name: " + dataFile.getAbsolutePath()); }
else { System.out.println("Reading phone book data..."); try { Scanner scanner = new Scanner( dataFile ); while (scanner.hasNextLine()) {
// Read one line from the file, containing one name/number pair.
String phoneEntry = scanner.nextLine(); int separatorPosition = phoneEntry.indexOf(’%’);
if (separatorPosition == -1) throw new IOException("File is not a phonebook data file."); name = phoneEntry.substring(0, separatorPosition); number = phoneEntry.substring(separatorPosition+1); phoneBook.put(name,number); } }
catch (IOException e) { System.out.println("Error in phone book data file."); System.out.println("File name: " + dataFile.getAbsolutePath()); System.out.println("This program cannot continue."); System.exit(1);
}
}

The program then lets the user do various things with the phone book, including making modifications. Any changes that are made are made only to the TreeMap that holds the data. When the program ends, the phone book data is written to the file (if any changes have been made while the program was running), using the following code:

if (changed) {
System.out.println("Saving phone directory changes to file " +
dataFile.getAbsolutePath() + " ...");
PrintWriter out;
try {
out = new PrintWriter( new FileWriter(dataFile) );
}
catch (IOException e) {
System.out.println("ERROR: Can’t open data file for output.");
return;
}
for ( Map.Entry<String,String> entry : phoneBook.entrySet() )
out.println(entry.getKey() + "%" + entry.getValue() );
out.close();
if (out.checkError())
System.out.println("ERROR: Some error occurred while writing data file.");
else
System.out.println("Done.");
}

The net effect of this is that all the data, including the changes, will be there the next time the program is run. I’ve shown you all the file-handling code from the program.

Files in GUI Programs

The previous examples in this article use a command-line interface, but graphical user interface programs can also manipulate files. Programs typically have an “Open” command that reads the data from a file and displays it in a window and a “Save” command that writes the data from the window into a file. We can illustrate this in Java with a simple text editor program, TrivialEdit.java. The window for this program uses a JTextArea component to display some text that the user can edit. It also has a menu bar, with a “File” menu that includes “Open” and “Save” commands. These commands are implemented using the techniques for reading and
writing files that were covered earlier.

When the user selects the Open command from the File menu in the TrivialEdit program, the program pops up a file dialog box where the user specifies the file. It is assumed that the file is a text file. A limit of 10000 characters is put on the size of the file, since a JTextArea is not meant for editing large amounts of text. The program reads the text contained in the specified file, and sets that text to be the content of the JTextArea. In this case, I decided to use a BufferedReader to read the file line-by-line. The program also sets the title bar of the window to show the name of the file that was opened. All this is done in the following method,
which is just a variation of the readFile() method. 

/**
* Carry out the Open command by letting the user specify a file to be opened
* and reading up to 10000 characters from that file. If the file is read successfully
* and is not too long, then the text from the file replaces the text in the JTextArea.
*/
public void doOpen() { if (fileDialog == null) fileDialog = new JFileChooser(); fileDialog.setDialogTitle("Select File to be Opened"); fileDialog.setSelectedFile(null); // No file is initially selected. int option = fileDialog.showOpenDialog(this); if (option != JFileChooser.APPROVE_OPTION)
return; // User canceled or clicked the dialog’s close box. File selectedFile = fileDialog.getSelectedFile(); BufferedReader in;
try { FileReader stream = new FileReader(selectedFile);
in = new BufferedReader( stream );
}
catch (Exception e) {
JOptionPane.showMessageDialog(this,
"Sorry, but an error occurred while trying to open the file:\n" + e);
return;
}
try {
String input = "";
while (true) { String lineFromFile = in.readLine();
if (lineFromFile == null)
break; // End-of-file has been reached.
input = input + lineFromFile + ’\n’;
if (input.length() > 10000)
throw new IOException("Input file is too large for this program.");
}
in.close();
text.setText(input);
editFile = selectedFile;
setTitle("TrivialEdit: " + editFile.getName());
}
catch (Exception e) {
JOptionPane.showMessageDialog(this,
"Sorry, but an error occurred while trying to read the data:\n" + e);
}
}

In this program, the instance variable editFile is used to keep track of the file that is currently being edited, if any, and the setTitle() method (from class JFrame) is used to set the title of the window to show the name of the file.

Similarly, the response to the Save command is a minor variation on the writeFile() method.  If you would like to see the rest of the program, including an example of using a Scanner to read integer-valued responses from the user, see the source code file, PhoneDirectoryFileDemo.java.

import java.io.*;

 


import java.util.Map;
import java.util.TreeMap; import java.util.Scanner;

/**
* This program lets the user keep a persistent "phone book" that contains names and phone numbers.  The data for the phone book is stored in a a file in the user's home directory.
* The program is meant only as a demonstration of file use.  The phone book data is stored is the form of Name/Number pairs, with little error checking.  In particular, the "phone directory" used in this program is not even close to what would be needed for a real phone book application.
*/

public class PhoneDirectoryFileDemo {
/**
* The name of the file in which the phone book data is kept.  The file is stored in the user's home directory.  The "." at the beginning of the file name means that the file will be a "hidden" file on Unix-based computers, including Linux and Mac OS X.
*/
private static String DATA_FILE_NAME = ".phone_book_demo"; public static void main(String[] args) {
String name, number;  // Name and number of an entry in the directory
// (used at various places in the program).

      TreeMap<String,String>  phoneBook;   // Phone directory data structure.
// Enties are name/number pairs.
phoneBook = new TreeMap<String,String>();
/* Create a dataFile variable of type File to represent the data file that is stored in the user's home directory.
*/
File userHomeDirectory = new File( System.getProperty("user.home") ); File dataFile = new File( userHomeDirectory, DATA_FILE_NAME );
/* If the data file already exists, then the data in the file is read and is used to initialize the phone directory.  The format of the file must be as follows:  Each line of the file represents one directory entry, with the name and the number for that entry separated by the character '%'.  If a file exists but does not have this format, then the program terminates; this is done to avoid overwriting a file that is being used for another purpose. */
if ( ! dataFile.exists() ) { System.out.println("No phone book data file found."); System.out.println("A new one will be created."); System.out.println("File name:  " + dataFile.getAbsolutePath()); }
else { System.out.println("Reading phone book data..."); try { Scanner scanner = new Scanner( dataFile ); while (scanner.hasNextLine()) { String phoneEntry = scanner.nextLine(); int separatorPosition = phoneEntry.indexOf('%');
if (separatorPosition == -1) throw new IOException("File is not a phonebook data file."); name = phoneEntry.substring(0, separatorPosition); number = phoneEntry.substring(separatorPosition+1); phoneBook.put(name,number); } }
catch (IOException e) { System.out.println("Error in phone book data file."); System.out.println("File name:  " + dataFile.getAbsolutePath()); System.out.println("This program cannot continue.");
System.exit(1);
} }

/* Read commands from the user and carry them out, until the user gives the "Exit from program" command.
*/
Scanner in = new Scanner( System.in ); boolean changed = false;  // Have any changes been made to the directory?

mainLoop: while (true) { System.out.println("\nSelect the action that you want to perform:"); System.out.println("   1.  Look up a phone number."); System.out.println("   2.  Add or change a phone number."); System.out.println("   3.  Remove an entry from your phone directory."); System.out.println("   4.  List the entire phone directory."); System.out.println("   5.  Exit from the program."); System.out.println("Enter action number (1-5):  ");
int command; if ( in.hasNextInt() ) { command = in.nextInt(); in.nextLine(); }
else { System.out.println("\nILLEGAL RESPONSE.  YOU MUST ENTER A NUMBER."); in.nextLine(); continue; }
switch(command) { case 1: System.out.print("\nEnter the name whose number you want to look up: "); name = in.nextLine().trim().toLowerCase(); number = phoneBook.get(name);
if (number == null) System.out.println("\nSORRY, NO NUMBER FOUND FOR " + name); else System.out.println("\nNUMBER FOR " + name + ":  " + number); break;
case 2: System.out.print("\nEnter the name: "); name = in.nextLine().trim().toLowerCase(); if (name.length() == 0) System.out.println("\nNAME CANNOT BE BLANK."); else if (name.indexOf('%') >= 0) System.out.println("\nNAME CANNOT CONTAIN THE CHARACTER \"%\".");
else { System.out.print("Enter phone number: "); number = in.nextLine().trim(); if (number.length() == 0) System.out.println("\nPHONE NUMBER CANNOT BE BLANK."); else { phoneBook.put(name,number); changed = true; } }
break;
case 3: System.out.print("\nEnter the name whose entry you want to remove: "); name = in.nextLine().trim().toLowerCase(); number = phoneBook.get(name); if (number == null) System.out.println("\nSORRY, THERE IS NO ENTRY FOR " + name); else { phoneBook.remove(name); changed = true; System.out.println("\nDIRECTORY ENTRY REMOVED FOR " + name); }
break; case 4: System.out.println("\nLIST OF ENTRIES IN YOUR PHONE BOOK:\n"); for ( Map.Entry<String,String> entry : phoneBook.entrySet() ) System.out.println("   " + entry.getKey() + ": " + entry.getValue() ); break;
case 5: System.out.println("\nExiting program.");
break mainLoop; default:
System.out.println("\nILLEGAL ACTION NUMBER."); } }
/* Before ending the program, write the current contents of the phone directory, but only if some changes have been made to the directory. */
if (changed) { System.out.println("Saving phone directory changes to file " + dataFile.getAbsolutePath() + " ..."); PrintWriter out; try {
out = new PrintWriter( new FileWriter(dataFile) ); }
catch (IOException e) { System.out.println("ERROR: Can't open data file for output."); return; }
for ( Map.Entry<String,String> entry : phoneBook.entrySet() ) out.println(entry.getKey() + "%" + entry.getValue() ); out.close();
if (out.checkError()) System.out.println("ERROR: Some error occurred while writing data file."); else System.out.println("Done."); } }

}

You can reach the author from  tarun.aror1@gmail.com email-id.








}