Solution for
Programming Exercise 10.2
THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to
the following exercise from this on-line
Java textbook.
Exercise 10.2:
Write a program that will count the number of lines in each
file that is specified on the command line. Assume that the
files are text files. Note that multiple files can be specified,
as in "java LineCounts file1.txt file2.txt file3.txt". Write
each file name, along with the number of lines in that file,
to standard output. If an error occurs while trying to read from
one of the files, you should print an error message for that file,
but you should still process all the remaining files.
Discussion
The main() routine for this program is a simple for loop
that processes the command-line arguments. Each argument is
supposed to be the name of a file. The loop just prints the
file name and calls a subroutine that counts the number of lines
in the file and outputs the result:
for (int i = 0; i < args.length; i++) {
System.out.print(args[i] + ": ");
countLines(args[i]);
}
The countLines() subroutine will catch any errors that
occur when it tries to access the file. If an error occurs,
it will output an error message instead of the number of lines.
Since the error is handled in the subroutine, it won't crash the
program or stop the main() routine from going on to process
any remaining files.
The countLines() subroutine creates a TextReader
stream to read from the file. It then calls the getln()
routine from the TextReader class to read lines of text from the
file, until the end of the file is encounters. It counts each line
as it reads it. In the end, the value of the counter is the
number of lines in the file. The subroutine writes this value
to standard output. All this is done in try...catch statements
so that an error can be handled if it occurs. It's pretty
straightforward. The complete solution is given below.
I do have a few comments, though.
The loop that reads and counts lines from the TextReader, in,
is given by
while (in.peek() != '\0') {
// Read the next line and count it.
in.getln();
lineCount++;
}
The function in.peek() looks ahead at the next character in
the stream. When the end-of-stream is reached, the value that is
returned is '\0', the character with code number zero. I should note
that this is not built into streams in general. It's just the way
that I designed the TextReader class. TextReaders
have another function, eof(), for testing for the
end of the stream, and it might be tempting to replace the
above loop with
while (in.eof() == false) {
// Read the next line and count it.
in.getln();
lineCount++;
}
Because of a technicality in the way that I designed TextReader
class, this won't work properly. When the eof() function is
called, it skips past spaces and blank lines before it tests for the
end-of-stream. The idea is to test whether there is anything left
in the stream besides whitespace. The problem is that the blank
lines that are skipped by eof() are not counted by the
above loop. In effect, the loop counts only the non-blank lines in
the file. Again, you should understand that this is not a problem
with Java streams, but only with the way that I designed the TextReader
class. I could easily have written eof() in such a way that
it would not skip blank lines, but then it would be less useful
in certain other contexts. This example also shows the
kinds of technicalities that can make text processing difficult
Another note, about the command line for calling this program.
If you are using UNIX or Linux, you can take advantage of something
called "wildcards" in the command-line arguments. For example,
if you say, "java LineCounts *.txt", the "*" is a wildcard.
The operating system will expand "*.txt" into a list of all the
files in the current directory that end in ".txt". Similarly,
"*xx*" expands into a list of all file names that contain "xx",
and "fil*dat" expands into the list of file names that begin with
"fil" and end with "dat". The "*" matches any number of characters
in the file name, including zero characters. This expansion
is done before your program sees the command-line arguments.
Typing the command "java LineCounts *.txt" would be exactly
equivalent to typing something like "java LineCounts file1.txt file2.txt
file3.txt". This type of expansion happens for any command
in UNIX, not just for the "java" command.
The Solution
/*
This program reports the number of lines in each of the files
that are specified as command line arguments. The files are
assumed to be text files. If a file does not exist, or if
some error occurs when the attempt is made to read the file,
then an error message is printed (but the other files are
still processed). This program depends on the non-standard
TextReader class.
*/
import java.io.*;
public class LineCounts {
public static void main(String[] args) {
// The main() routine simply gets the file names from the
// command line and calls the countLines() routine to process
// each name. Since any errors are handled in the countLines()
// routine, the main program will continue after an error occurs
// while trying to process one of the files.
if (args.length == 0) {
// This program must have at least one command-line
// argument to work with.
System.out.println("Usage: java LineCounts <file-names>");
return;
}
for (int i = 0; i < args.length; i++) {
System.out.print(args[i] + ": ");
countLines(args[i]);
}
} // end main()
static void countLines(String fileName) {
// Count the number of lines in the specified file, and
// print the number to standard output. If an error occurs
// while processing the file, print an error message instead.
// Two try...catch statements are used so I can give a
// different error message in each case.
TextReader in; // A stream for reading from the file.
int lineCount; // Number of lines in the file.
try {
in = new TextReader( new FileInputStream(fileName) );
}
catch (Exception e) {
System.out.println("Error. Can't open file.");
return;
}
lineCount = 0;
try {
while (in.peek() != '\0') {
// Read the next line and count it.
in.getln();
lineCount++;
}
}
catch (Exception e) {
System.out.println("Error. Problem with reading from file.");
return;
}
System.out.println(lineCount);
} // end countLines()
} // end class LineCounts
[ Exercises
| Chapter Index
| Main Index
]