Chapter: Java The Complete Reference - The Java Language - I/O, Applets, and Other Topics

Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail

Reading and Writing Files - Java

Java provides a number of classes and methods that allow you to read and write files. Before we begin, it is important to state that the topic of file I/O is quite large and file I/O is examined in detail in Part II.

Reading and Writing Files

 

Java provides a number of classes and methods that allow you to read and write files. Before we begin, it is important to state that the topic of file I/O is quite large and file I/O is examined in detail in Part II. The purpose of this section is to introduce the basic techniques that read from and write to a file. Although bytes streams are used, these techniques can be adapted to the character-based streams.

 

Two of the most often-used stream classes are FileInputStream and FileOutputStream, which create byte streams linked to files. To open a file, you simply create an object of one of these classes, specifying the name of the file as an argument to the constructor. Although both classes support additional constructors, the following are the forms that we will be using:

 

FileInputStream(String fileName) throws FileNotFoundException FileOutputStream(String fileName) throws FileNotFoundException

Here, fileName specifies the name of the file that you want to open. When you create an input stream, if the file does not exist, then FileNotFoundException is thrown. For output streams, if the file cannot be opened or created, then FileNotFoundException is thrown. FileNotFoundException is a subclass of IOException. When an output file is opened, any preexisting file by the same name is destroyed.

When you are done with a file, you must close it. This is done by calling the close( ) method, which is implemented by both FileInputStream and FileOutputStream. It is shown here:

 

void close( ) throws IOException

 

Closing a file releases the system resources allocated to the file, allowing them to be used by another file. Failure to close a file can result in “memory leaks” because of unused resources remaining allocated.

Before moving on, it is important to point out that there are two basic approaches that you can use to close a file when you are done with it. The first is the traditional approach, in which close( ) is called explicitly when the file is no longer needed. This is the approach used by all versions of Java prior to JDK 7 and is, therefore, found in all pre-JDK 7 legacy code. The second is to use the try-with-resources statement added by JDK 7, which automatically closes a file when it is no longer needed. In this approach, no explicit call to close( ) is executed. Since there is a large amount of pre-JDK 7 legacy code that is still being used and maintained, it is important that you know and understand the traditional approach. Therefore, we will begin with it. The new automated approach is described in the following section.

 

To read from a file, you can use a version of read( ) that is defined within FileInputStream. The one that we will use is shown here:

 

int read( ) throws IOException

 

Each time that it is called, it reads a single byte from the file and returns the byte as an integer value. read( ) returns –1 when the end of the file is encountered. It can throw an

 

IOException.

The following program uses read( ) to input and display the contents of a file that contains ASCII text. The name of the file is specified as a command-line argument.

 

/* Display a text file.

 

To use this program, specify the name of the file that you want to see.

 

For example, to see a file called TEST.TXT, use the following command line.

 

java ShowFile TEST.TXT

 

*/

 

import java.io.*;

 

class ShowFile {

 

public static void main(String args[])

 

{

 

int i; FileInputStream fin;

 

     First, confirm that a filename has been specified.

     if(args.length != 1) {

 

System.out.println("Usage: ShowFile filename"); return;

 

}

 

     Attempt to open the file.

 

try {

 

fin = new FileInputStream(args[0]);

 

} catch(FileNotFoundException e) {

System.out.println("Cannot Open File"); return;

 

}

 

     //At this point, the file is open and can be read.

 

     //The following reads characters until EOF is encountered.

 

     try {

 

do {

 

     = fin.read();

 

if(i != -1) System.out.print((char) i);

 

} while(i != -1);

 

} catch(IOException e) {

System.out.println("Error Reading File");

}

 

// Close the file.

try {

 

fin.close();

 

} catch(IOException e) {

System.out.println("Error Closing File");

}

 

}

 

}

In the program, notice the try/catch blocks that handle the I/O errors that might occur. Each I/O operation is monitored for exceptions, and if an exception occurs, it is handled. Be aware that in simple programs or example code, it is common to see I/O exceptions simply thrown out of main( ), as was done in the earlier console I/O examples. Also, in some real-world code, it can be helpful to let an exception propagate to a calling routine to let the caller know that an I/O operation failed. However, most of the file

 

I/O examples in this book handle all I/O exceptions explicitly, as shown, for the sake of illustration.

Although the preceding example closes the file stream after the file is read, there is a variation that is often useful. The variation is to call close( ) within a finally block. In this approach, all of the methods that access the file are contained within a try block, and the finally block is used to close the file. This way, no matter how the try block terminates, the file is closed. Assuming the preceding example, here is how the try block that reads the file can be recoded:

 

 

try { do {

 

i = fin.read();

 

if(i != -1) System.out.print((char) i);

 

} while(i != -1);

 

} catch(IOException e) {

System.out.println("Error Reading File");

} finally {

 

// Close file on the way out of the try block.

try {

 

fin.close();

 

} catch(IOException e) {

System.out.println("Error Closing File");

 

}

 

}

 

Although not an issue in this case, one advantage to this approach in general is that if the code that accesses a file terminates because of some non-I/O related exception, the file is still closed by the finally block.

Sometimes it’s easier to wrap the portions of a program that open the file and access the file within a single try block (rather than separating the two) and then use a finally block to close the file. For example, here is another way to write the ShowFile program:

 

/* Display a text file.

 

To use this program, specify the name of the file that you want to see.

 

For example, to see a file called TEST.TXT, use the following command line.

 

java ShowFile TEST.TXT

 

This variation wraps the code that opens and accesses the file within a single try block. The file is closed by the finally block.

 

*/

import java.io.*;

 

class ShowFile {

 

public static void main(String args[])

 

{

 

int i;

 

FileInputStream fin = null;

 

     First, confirm that a filename has been specified.

     if(args.length != 1) {

 

System.out.println("Usage: ShowFile filename"); return;

 

}

 

     //The following code opens a file, reads characters until EOF is encountered, and then closes the file via a finally block.

     try {

 

fin = new FileInputStream(args[0]);

 

do {

 

i = fin.read();

 

if(i != -1) System.out.print((char) i);

 

} while(i != -1);

 

} catch(FileNotFoundException e) { System.out.println("File Not Found.");

 

} catch(IOException e) { System.out.println("An I/O Error Occurred");

} finally {

 

// Close file in all cases.

try {

 

if(fin != null) fin.close(); } catch(IOException e) {

 

System.out.println("Error Closing File");

 

}

 

}

 

}

 

}

 

In this approach, notice that fin is initialized to null. Then, in the finally block, the file is closed only if fin is not null. This works because fin will be non-null only if the file is successfully opened. Thus, close( ) is not called if an exception occurs while opening the file.

It is possible to make the try/catch sequence in the preceding example a bit more compact. Because FileNotFoundException is a subclass of IOException, it need not be caught separately. For example, here is the sequence recoded to eliminate catching FileNotFoundException. In this case, the standard exception message, which describes the error, is displayed.

 

try {

 

fin = new FileInputStream(args[0]);

 

do {

i = fin.read();

 

if(i != -1) System.out.print((char) i);

 

} while(i != -1);

 

} catch(IOException e) {

System.out.println("I/O Error: " + e);

 

} finally {

 

// Close file in all cases.

try {

 

if(fin != null) fin.close(); } catch(IOException e) {

System.out.println("Error Closing File");

 

}

 

}

 

In this approach, any error, including an error opening the file, is simply handled by the single catch statement. Because of its compactness, this approach is used by many of the I/O examples in this book. Be aware, however, that this approach is not appropriate in cases in which you want to deal separately with a failure to open a file, such as might be caused if a user mistypes a filename. In such a situation, you might want to prompt for the correct name, for example, before entering a try block that accesses the file.

 

To write to a file, you can use the write( ) method defined by FileOutputStream. Its simplest form is shown here:

 

void write(int byteval) throws IOException

 

This method writes the byte specified by byteval to the file. Although byteval is declared as an integer, only the low-order eight bits are written to the file. If an error occurs during writing, an IOException is thrown. The next example uses write( ) to copy a file:

 

/* Copy a file.

 

To use this program, specify the name

 

of the source file and the destination file. For example, to copy a file called FIRST.TXT to a file called SECOND.TXT, use the following command line.

 

java CopyFile FIRST.TXT SECOND.TXT

 

*/

 

import java.io.*;

 

class CopyFile {

 

public static void main(String args[]) throws IOException

 

{

 

int i;

 

FileInputStream fin = null; FileOutputStream fout = null;

 

// First, confirm that both files have been specified.

if(args.length != 2) {

 

System.out.println("Usage: CopyFile from to"); return;

}

 

// Copy a File.

try {

 

// Attempt to open the files.

 

fin = new FileInputStream(args[0]);

fout = new FileOutputStream(args[1]);

 

do {

 

i = fin.read();

 

if(i != -1) fout.write(i);

 

} while(i != -1);

 

} catch(IOException e) {

System.out.println("I/O Error: " + e);

 

} finally { try {

 

if(fin != null) fin.close(); } catch(IOException e2) {

System.out.println("Error Closing Input File");

 

}

 

try {

 

if(fout != null) fout.close();

 

} catch(IOException e2) {

System.out.println("Error Closing Output File");

 

}

 

}

 

}

 

}

In the program, notice that two separate try blocks are used when closing the files. This ensures that both files are closed, even if the call to fin.close( ) throws an exception.

In general, notice that all potential I/O errors are handled in the preceding two programs by the use of exceptions. This differs from some computer languages that use error codes to report file errors. Not only do exceptions make file handling cleaner, but they also enable Java to easily differentiate the end-of-file condition from file errors when input is being performed.

 


Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail


Copyright © 2018-2020 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.