Chapter 22. File I/O
Most real programs must access files at some point. We'll investigate a couple of simple ways to work with files using Java's libraries.
22.1. The char
type
But first, we will study the char
type, a primitive type built
into Java for representing a single character. This is a new primitive
type, in addition to the three we've already seen: int
,
double
, and boolean
.
A character is any single symbol that might appear
in a sentence, such as a letter in the English alphabet (a or
A, for
example), a single digit (0), a punctuation mark (%),
or a letter in some other alphabet (the Hebrew letter aleph ℵ).
We can reference any character value in Java by enclosing it in single
quotation marks: 'a'
.
char letter = 'a';
For beginners, the distinction between char
and String
can be a little confusing, but with practice it's an easy distinction to
make: The char
type is for a single character, while the
String
type represents a sequence of characters (though
admittedly the sequence may have just one character). We use
single-quotation marks for char
values and double-quotation marks
for String
values.
When we have a variable referencing a char
, we can convert it
to a String
by adding it to the empty String.
String word = "" + letter;
And when we have a variable referencing a String
, we can
retrieve a single characte from it using its charAt
method.
This method takes a single int
parameter giving the index of the
character we want from the String
, and it returns that
char
.
char firstLetter = word.charAt(0);
In Chapter 8 we saw a program that read a line from the
user and displayed that line in reverse. It used the substring
method to retrieve each individual string, since that is all we had
seen. Using the charAt
method is easier than substring
,
though, when we want to retrieve just a single character.
String str = readLine("Type a string to reverse: ");
int toPrint = str.length() - 1;
while(toPrint >= 0) {
print(str.charAt(toPrint));
toPrint--;
}
Because the char
type is a primitive type, Java programs
can compare char
values using the ==
operator,
as opposed to the String
type, which is a class and whose values
should be compared using the equals
method.
int count = 0;
for(int i = 0; i < str.length(); i++) {
if(str.charAt(i) == 'r') count++;
}
22.2. Elementary file manipulation
Java includes a number of classes for reading and writing files. Two
of the most elementary are FileReader
and FileWriter
,
both in the java.io
package.
|
|
These are very elementary classes that aren't often very convenient to use, but they can be used for simple operations like copying all the characters in one file into another file, as in Figure 22.1.
1 import acm.program.Program;
2 import java.io.FileReader; import java.io.FileWriter;
3 import java.io.FileNotFoundException; import java.io.IOException;
4
5 public class FileCopy extends Program {
6 public void run() {
7 // open input file 'src' and output file 'dst'
8 FileReader src;
9 try {
10 String srcName = readLine("Source file name? ");
11 src = new FileReader(srcName);
12 } catch(FileNotFoundException e) {
13 System.out.println("Cannot open source file");
14 return;
15 }
16
17 FileWriter dst;
18 try {
19 String dstName = readLine("Destination file name? ");
20 dst = new FileWriter(dstName);
21 } catch(IOException e) {
22 System.out.println("Cannot open destination file");
23 return;
24 }
25
26 try {
27 // repeatedly fill array from 'src' and save into 'dst'
28 char[] buf = new char[128];
29 while(true) {
30 int n = src.read(buf);
31 if(n < 0) break; // end of file reached
32 dst.write(buf, 0, n);
33 }
34
35 // close files
36 src.close();
37 dst.close();
38 } catch(IOException e) {
39 System.out.println("I/O error: Copy may be incomplete");
40 }
41 }
42 }
You can see that we have to use several try
blocks
to deal with all the exceptions that can arise in the process of working
with files. These try
blocks are required by Java:
An IOException
is a checked exception, so the Java compiler
forces the programmer to deal with the exception somehow.
The core of the FileCopy
program is in
lines 29
to 33.
Here, we repeatedly fill the buffer buf
from the source file,
letting n
be the number of characters placed into the buffer.
And then we dump that many characters from buf
into the
destination file before repeating. Eventually, the FileReader
will have exhausted the source file, and so its read
method would
return −1; at that point, the program breaks out of the loop so
that both files can be closed.
22.3. Text file manipulation
In practice, dealing with files using FileReader
and
FileWriter
is often a pain; we often want to process the file as
a sequence of strings rather than as a sequence of characters.
Two classes Scanner
(found in java.util
)
and PrintWriter
(found in java.io
) are more helpful.
|
|
The FileSearch
program of Figure 22.2
illustrates these two classes at work.
1 import acm.program.Program;
2 import java.io.FileReader; import java.io.FileWriter;
3 import java.io.FileNotFoundException; import java.io.IOException;
4 import java.io.PrintWriter; import java.util.Scanner;
5
6 public class FileSearch extends Program {
7 public void run() {
8 // open input file 'src' and output file 'dst'
9 Scanner src;
10 try {
11 String srcName = readLine("Source file name? ");
12 src = new Scanner(new FileReader(srcName);
13 } catch(FileNotFoundException e) {
14 System.out.println("Cannot open source file");
15 return;
16 }
17
18 PrintWriter dst;
19 try {
20 String dstName = readLine("Destination file name? ");
21 dst = new PrintWriter(new FileWriter(dstName));
22 } catch(IOException e) {
23 System.out.println("Cannot open destination file");
24 return;
25 }
26
27 // determine what string to search for
28 String toFind = readLine("What text should we search for? ");
29
30 // repeatedly read line from 'src' and, if it contains the
31 // desired string, save it into 'dst'
32 while(src.hasNextLine()) {
33 String line = src.nextLine();
34 if(line.indexOf(toFind) >= 0) dst.println(line);
35 }
36
37 // close files
38 src.close();
39 dst.close();
40 }
41 }