-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathex2-ans.html
More file actions
207 lines (172 loc) · 8.43 KB
/
ex2-ans.html
File metadata and controls
207 lines (172 loc) · 8.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Javanotes 9, Solution to Exercise 2, Chapter 11</title>
<link type="text/css" rel="stylesheet" href="../javanotes.css">
</head>
<body>
<div class="page">
<div align="right">
<small>
[ <a href="exercises.html">Exercises</a> |
<a href="index.html">Chapter Index</a> |
<a href="../index.html">Main Index</a> ]
</small>
</div>
<hr>
<div class="content">
<h2>Solution for Programming Exercise 11.2</h2>
<hr class="break">
<p>
<span class="start"><big>T</big>his page contains</span> a sample solution to
one of the exercises from <a href="../index.html">Introduction to Programming Using Java</a>.</p>
<hr>
<h3 class="exercise">Exercise 11.2:</h3>
<p>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:</p>
<pre>java LineCounts file1.txt file2.txt file3.txt</pre>
<p>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. Do not use
<span class="classname">TextIO</span> to process the files; use a <span class="classname">Scanner</span> or a
<span class="classname">BufferedReader</span> to process each file.</p>
<hr>
<div class="exercisesubtitle" align="center">
<big><b>Discussion</b></big>
</div>
<hr>
<p>The <span class="code">main()</span> 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:</p>
<pre>for (int i = 0; i < args.length; i++) {
System.out.print(args[i] + ": ");
countLines(args[i]);
}</pre>
<p>The <span class="code">countLines()</span> 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 <span class="code">main()</span> routine from
going on to process any remaining files.</p>
<p>The <span class="code">countLines()</span> subroutine can be written using either a
a <span class="classname">BufferedReader</span>
or a <span class="classname">Scanner</span> to read lines from the file; each of these
classes has a simple method for reading a complete line of text from the file.
(An alternative approach would read individual characters from the file and
count the number of end-of-line markers. Unfortunately, ends-of-line can be
marked by any of the strings "\n", "\r", or "\r\n" and that would add to the
difficulty of the task. The line reading methods in
<span class="classname">BufferedReader</span> and <span class="classname">Scanner</span> will
work no matter which end-of-line marker is used.)</p>
<p>In my solution, I use a <span class="classname">BufferedReader</span>.
The <span class="code">countLines()</span> method creates a <span class="classname">BufferedReader</span> stream to
read from the file. It then calls the <span class="code">readLine()</span> routine from the
<span class="classname">BufferedReader</span> class to read lines of text from the file, until the end of
the file is encountered. For a <span class="classname">BufferedReader</span>, the
end-of-file can be recognized when <span class="code">readLine()</span> returns <span class="code">null</span>.
The method 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 a <span class="code">try..catch</span>
statement, treating the <span class="classname">BufferedReader</span> as a resource,
so that an error can be handled if it occurs. It's pretty
straightforward. The complete solution is given below.</p>
<p>An interesting variation on this would be to use the stream API from
<a href="../c10/s6.html">Section 10.6</a>. A <span class="classname">BufferedReader</span>,
<span class="code">in</span>, has a method <span class="code">in.lines()</span> that creates
a stream of strings containing the lines of the file. We can find out
how many lines there are just by applying the <span class="code">count()</span> stream
operation to that stream. So, using the stream API, the lines could
be counted using</p>
<pre>long lineCt = in.lines().count();</pre>
<p>One note about the command line for calling this program. If you are
using UNIX, including MacOS or Linux, you can take advantage of something called "wildcards" in
the command-line arguments. For example, if you say, "<span class="code">java LineCounts *.txt</span>",
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, so
you don't have to worry about it in your program.
Typing the command "<span class="code">java LineCounts *.txt</span>" would be exactly equivalent
to typing something like "<span class="code">java LineCounts file1.txt file2.txt
file3.txt</span>". This type of expansion happens for any command in UNIX, not
just for the "java" command.</p>
<p>I should also note that if a file name contains spaces or other special
characters, then the file name should be enclosed in quotation marks when
it is used as an argument on the command line.</p>
<hr>
<div class="exercisesubtitle" align="center">
<big><b>The Solution</b></big>
</div>
<hr>
<pre class="exercisecode">import java.io.*;
/**
* 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).
*/
public class LineCounts {
/**
* 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.
*/
public static void main(String[] args) {
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()
/**
* 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.
*/
private static void countLines(String fileName) {
try(BufferedReader in = new BufferedReader( new FileReader(fileName) )) {
int lineCount = 0; // number of lines read from the file
String line = in.readLine(); // Read the first line.
while (line != null) {
lineCount++; // Count this line.
line = in.readLine(); // Read the next line.
}
System.out.println(lineCount + " lines");
}
catch (FileNotFoundException e) {
System.out.println("Error. Can't open file.");
}
catch (Exception e) {
System.out.println("Error. Problem with reading from file.");
}
} // end countLines()
} // end class LineCounts
</pre>
</div>
<hr>
<div align="right">
<small>
[ <a href="exercises.html">Exercises</a> |
<a href="index.html">Chapter Index</a> |
<a href="../index.html">Main Index</a> ]
</small>
</div>
</div>
</body>
</html>