(Updated January 19, 2025)
Table of contents
-
Overview
The
while
loopRange-based or Counter-based loop
Sentinel-based loop
Flag-based loop
EOF-based loop
The
for
loopThe
do-while
loopThe
break
and continue
statementsNested Loops
File Handling
Quiz
Building a Command Line Interface
Exercises
Overview
There is a clear need for repetition in many things during our daily life. These may include cooking in the kitchen (3 meals, perhaps more), doing laundry (2 or more loads on a Saturday afternoon), broadcast television (show intro to sitcom, then alternate commercials/sitcom until end of the show), or simply mowing the lawn (walk mower to end of the property, turn, walk mower back to the other end, repeat). It is straightforward to see that people operate iteratively rather naturally.
Consider the following Java’d-up pseudocode for mowing a lawn where fictitious variables and methods have been liberally created for entertainment purposes:
mower.removeFromStorage();
if ( mower.oldClippings == high ) {
mower.cleanCuttingChamber();
mower.notifyOwnerOfBadMaintenancePractice();
}
if ( mover.bladeStatus != mowerConstants.ADEQUATE ) {
Blade replacementBlade = new Blade();
mower.changeBlade(replacementBlade);
}
if ( mower.fuelLevel != mowerConstants.FULL )
mower.refuel();
while ( mower.motorRunning != true )
mower.startMower();
do {
mower.walkToEndOfLawn();
mower.turn();
lawn.checkLawnStatus();
} while ( lawn.mowed != true );
Now for some fun with this code. There are several selection statements involved in preparing the work to be done. This is very typical of programming with iteration or programming with loops.
Once the preliminary checks are performed, there is the while
loop to start the mower. The idea is that the startMower()
method will set the boolean
variable motorRunning
to true
if the mower starts and stays running. That means that while the mower is not running, we should continue to try and start it. Now, in the real world, the average person would try only a few times, especially if its a pull-start, and then begin to look for things like a flooded engine, a bad float, a fouled spark plug, or simply stop to catch their breath, then try some more. That part of our program had been omitted but could easily be added in.
Now we introduce the concept of testing before performing repetition. In the case of starting the engine, we checked to ensure it wasn’t running already by the parenthesized test to the right of the while
keyword. This constitutes a top-test loop meaning that we may perform the loop iterations zero or more times based on the result of that test. Another way to say it is: while something is true, perform the work.
There is a second loop example in our pseudocode. It is a do
/while
loop. The concept of this loop is to introduce a bottom-test loop. These loops are useful when we know we will perform the work inside the loop at least once. Only after the work is done once will we test to see if additional applications of the same work are necessary. Now we can say: mow a strip of lawn, turn and check lawn status while there is still lawn to be mowed.
The mowing of the lawn lends itself well to the bottom-test concept since we started out knowing that our virtual yard needed cutting; otherwise, we would not be there with our virtual mower.
It is important to note the subtlety in our two loops:
- While the mower is not running, start the mower.
- Mow a strip of lawn and turn while the mowing is incomplete.
Given different circumstances, we could have used do-while
for the starting of the mower or a while
for the cutting. The circumstances of the situation dictate how we will choose our looping methods.
This is carried over to programming, where conceptually, we would want to do the same as our mowing example. Consider the need to read three values from the user to derive a sum and average of those three values. We could simply write:
int value1, value2, value3;
double avg;
System.out.print("Enter value #1: ");
value1 = kb.nextInt();
System.out.print("Enter value #2: ");
value2 = kb.nextInt();
System.our.print("Enter value #3: ");
value3 = kb.nextInt();
sum = value1 + value2 + value3;
avg = sum / 3.0;
This method works. Maybe this is not much typing for the average user, but extend the concept to 5 values. Ten values. 100 values. This suddenly becomes unwieldy the higher we go. We are also adding a lot of additional variables. We need to make this into a loop.
We can start by eliminating the idea of value1
, value2
or valuen
and just have a single variable called value
that we will recycle it through each iteration of the loop. We will also need to take into consideration that the calculation of sum
is no longer a job to be done after all the values are read in from the user. Instead, we will need to incorporate the idea of a running-total while we continue to read values.
One last piece, which is just maintaining continuity, incorporates the numeric value in the prompt just as our previous brute-force method did. Consider the following while
loop.
int sum = 0;
int x, value;
double avg;
Scanner kb = new Scanner(System.in);
x = 1;
while ( x <= 3 ) {
System.out.print("Enter value #" + x + ": ");
value = kb.nextInt();
sum = sum + value;
x++;
}
avg = sum / 3.0;
We start by setting sum
to zero since we have seen no values yet. This was unnecessary in our previous example since the sum was calculated after all the values were read from the user.
Next, the variable x
is set to 1. The variable x
will be used as our loop control variable. In other words, x
is the variable we will examine to determine if more work remains or if we have completed our iterations and can stop the loop.
The loop starts with a test of x <= 3
or, more to the point, 1 <= 3 since x
is 1. Since that test is true, we enter the loop and begin our work.
value
since we have no reason to keep all the values. As an alternative Chapter 9 covers how to keep all the values in an array.The loop will perform work for values of x
equal to 1, 2, and 3. Each time, we go back to the top of the loop to reevaluate the condition that allows us to stay.
When the value of x
reaches 4, the test at the top will fail ( 4 <= 3 ). At that point, we fall out of the loop to the statement just below the loop itself, and the program continues by determining the average.
Note that each time the loop performs work, we update the value of x
. The increment operator bumps the value of x
by one before we retest its value at the top.
Now that we have seen a basic example of iteration, we introduce the three loop constructs:
- The
while
loop - The
for
loop. - The
do-while
loop
Each loop construct will be presented in detail, starting with the while
loop.
The while
Loop
As we saw in the overview section, many repetitive tasks can be formed into iterative constructs. The overview touches on the concept of the top-test loop construct. Now, we will examine multiple variations of the loop and how to control the work that needs to be done.
First, let us discuss the three parts of a loop. These are initialization, condition and update. The initialization is the work necessary to prepare for the iterative process. The condition determines if the iterative process should continue. Finally, the update portion assists in altering the loop control variable to ensure that the condition will eventually fail and cause the loop to stop if that is our goal. One loop that never stops, known as the infinite loop, is usually unintentional but is suitable and necessary for things like printer queues and web servers.
Range-based or Counter-based loop
The range-based loop is used when we know that something will occur a predetermined number of times. The loop control variable keeps track of how many times the work has been done.
The previous loop example is repeated here as a complete program with the details of initialization, condition, and update highlighted for clarity.
import java.util.Scanner;
public class RangeWhile {
static Scanner kb = new Scanner(System.in);
public static void main(String[] args) {
int sum = 0;
int x, value;
double avg;
// loop iterates from 1 to 3, inclusive.
x = 1; // initialization
while ( x <= 3 ) { // condition
System.out.print("Enter value #" + x + ": ");
value = kb.nextInt();
sum = sum + value;
x++; //update
}
avg = sum / 3.0;
System.out.println("The sum is " + sum + ".");
System.out.println("The average is " + avg + ".");
}
}
Example 1a: Range-based loop using while
.
A few details worth mentioning about Example 1a include:
- The loop will end its iterations when
x
exceeds 3. - We do the prompting and reading inside the
while
loop at the top. - Calculations and displaying results only occur when we know the answer. That is after the loop is complete.
Sentinel-based loop
Sentinel-based loops are used when we are expecting a specific value or values that the user must enter to indicate that the loop may cease. If we needed the user to enter -1 as a value to terminate the loop, we could craft something like this:
import java.util.Scanner;
public class SentinelWhile {
static Scanner kb = new Scanner(System.in);
public static void main(String[] args) {
int sum = 0, count = 0;
int x, value;
double avg;
// prompt user an indicate how to end.
System.out.print("Enter integers (-1 to end): ");
value = kb.nextInt(); //initialization
while ( value != -1 ) { //condition
sum = sum + value;
count++;
System.out.print("Enter integers (-1 to end): ");
value = kb.nextInt(); //update
}
// average calculated after loop is complete.
avg = sum / (double)count;
System.out.println("The sum is " + sum + ".");
System.out.println("The average is " + avg + ".");
}
}
Example 1b: Sentinel-based loop using while
.
There are a few things to note about Example 1b include:
- The prompt appears before the loop and again at the bottom of the loop.
- The call to
nextInt()
, just like the prompt is located both before the loop and again at the end of the loop. - The loop control variable is
value
. - The same statement is used to initialize and to update the loop control variable.
- Because we stop when we see -1, we need to count the number of values read in order to know the average.
We must include the prompt twice if we want the user to continue to be prompted. Once the loop begins, we cannot break the confines of the loop to get to the original first prompt. More importantly, the prompting and reading of the following value happen at the bottom of the loop to alter the loop control variable. This is necessary before reapplying the test when we reach the top of the loop. Otherwise, we run the risk of an infinite loop.
It is not at all unusual, as we saw in the previous example, for the update statements to be the same statements that initialized our loop control variable. In the example above, the only way to alter the loop control variable is to read another value from the user. Since the loop is driven entirely by user input, there is often little or no choice.
Flag-based loop
The flag-based loop uses a boolean
variable for loop control. To demonstrate, let us create a complete program to ask the user to guess a random number between 1 and 20 inclusive, as shown in Example 1.
import java.util.Scanner;
public class Guess {
static Scanner kb = new Scanner(System.in);
public static void main(String[] args) {
int randomNumber, guess;
boolean found;
// random number between 1 and 20
randomNumber = (int)(Math.random() * 20 + 1);
System.out.println("I'm thinking of a number between 1 and 20.");
found = false;
// while not found, guess forever.
while ( ! found ) {
System.out.print("Guess the value between 1 and 20: ");
guess = kb.nextInt();
if ( guess == randomNumber )
found = true;
else
System.out.println("It's not " + guess + ".\n");
}
System.out.println("\nYou got it!\n");
}
}
Example 1c: Flag-based while loop.
A few things to note about Example 1c include:
- The boolean variable,
found
, needs to be initialized tofalse
before the loop begins. - Using
Math.random()
coerces the result to adouble
, therefore, we cast the result toint
. - We are not concerned with guesses that are out of bounds or with a limit on the number of guesses a user is allowed.
- We read values from the user inside the loop with no special attention made to how or when we prompt since
found
is the loop control variable and notguess
.
The program in Example 1d is reworked slightly to deal with a limit on guesses and inform the user of the value if they are unsuccessful in guessing the correct answer.
import java.util.Scanner;
public class BetterGuess {
static Scanner kb = new Scanner(System.in);
public static void main(String[] args) {
int randomNumber, guess, guessLimit=5;
boolean found;
// random number between 1 and 20
randomNumber = (int)(Math.random() * 20 + 1);
System.out.println("I'm thinking of a number between 1 and 20.");
found = false;
// while not found, guess within limit.
while ( ! found && guessLimit > 0 ) {
System.out.println("You have " + guessLimit + " guesses remaining.");
System.out.print("Guess the value between 1 and 20: ");
guess = kb.nextInt();
guessLimit--;
if ( guess == randomNumber )
found = true;
else
System.out.println("It's not " + guess +".\n");
}
if ( found )
System.out.println("\nYou got it!\n");
else
System.out.println("\nThe number was " + randomNumber + ".\n");
}
}
Example 1d: Improved number guessing program.
The details of this modification are:
- The variable
guessLimit
has been set to 5 and will count down to zero. guessLimit
is decremented after eachguess
is read from the user.- We still lack code to deal with out-of-bounds guesses, but could easily be added after each
guess
. Should an out-of-bound guess count against the user? - We have added the
&&
logical operator to account for two conditions that need to be met to continue prompting for guesses. They must not have discovered the answer, and they must also have guesses remaining.
EOF-based loop
The EOF-based loop works with the hasNext()
method of the Scanner
class. The idea is to detect the end of the file (EOF), which is more to the point, the end of input. The user can enter CTRL-D in Unix or CTRL-Z in DOS/Windows to trigger the EOF and terminate the loop. Consider the next program segment to read lines of text from the user and simply display the length of each string.
import java.util.Scanner;
public class EOFWhile {
static Scanner kb = new Scanner(System.in);
public static void main(String[] args) {
String line;
// prompt here to prep for the loop.
System.out.print("Enter a string (CTRL-D to end): ");
// hasNext() pauses here, waiting to see if there's input
while ( kb.hasNext() ) {
line = kb.nextLine(); // read the line
System.out.println("Input was " + line.length() + " characters long.");
// prompt again as we return to the top.
System.out.print("Enter a string (CTRL-D to end): ");
}
}
}
Example 1e: The EOF-based while
loop.
Items to note about Example 1e are:
hasNext()
helps to determine if the loop should continue (is there is more to read?).- We are prompting before the loop and at the bottom of the loop.
- The reading of input happens inside the loop and not automatically through the use of
hasNext()
.
The for
Loop
Another top-test loop is the for
loop and is generally used for range-based solutions. This loop can be tricky to grasp at first glance, but once you realize it is simply a reworked while
loop, it's a snap.
Since our loops still have the initialization, condition test, and update components, the for
loop chooses to put them all on display on the same line rather than spreading them out.
Let us take to code from Example 1a and repeat it here, then convert it into a for
loop.
import java.util.Scanner;
public class RangeWhile {
static Scanner kb = new Scanner(System.in);
public static void main(String[] args) {
int sum = 0;
int x, value;
double avg;
// loop iterates from 1 to 3, inclusive.
x = 1; // initialization
while ( x <= 3 ) { // condition
System.out.print("Enter value #" + x + ": ");
value = kb.nextInt();
sum = sum + value;
x++; //update
}
avg = sum / 3.0;
System.out.println("The sum is " + sum + ".");
System.out.println("The average is " + avg + ".");
}
}
Example 2a: Range-based loop using while
.
The for
loop version looks like this:
import java.util.Scanner;
public class RangeFor {
static Scanner kb = new Scanner(System.in);
public static void main(String[] args) {
int sum = 0;
int x, value;
double avg;
// loop iterates from 1 to 3, inclusive.
for ( x = 1; x <= 3; x++ ) {
System.out.print("Enter value #" + x + ": ");
value = kb.nextInt();
sum = sum + value;
}
avg = sum / 3.0;
System.out.println("The sum is " + sum + ".");
System.out.println("The average is " + avg + ".");
}
}
Example 2b: Range-based loop using for
.
The highlighted portions show what has changed. Let us itemize the differences:
- Used the
for
reserved word in place ofwhile
. - Moved all three loop components to the parenthetic that previously only held the condition test.
- Used semicolons to separate the initialization, condition test, and update.
The initialization only occurs once and before the condition test is applied. Entering the loop is still determined by the successful evaluation of the condition test, just as it is with the while
loop.
The update, however, is a little different. This will always happen as the last statement before reapplying the condition test at the top of the loop. With our while
loop, the update could have been any statement within the loop's body. We chose to do it at the bottom of the while
loop since that made the most sense.
The for
loop enforces it at the bottom when the update is specified in the parenthesized portion.
You may leave out any or all of the parenthesized components of the for
loop preserving the semicolons.
for (;;) {
/**
* This is an infinite loop that will
* execute this code forever!
*/
}
This intentionally creates an infinite loop. Be sure this is your goal.
In Example 2c, we use a for loop to examine and manipulate a String.
public class Reverse {
public static void main(String[] args) {
String s = "Fox on the lawn.", rev;
int x;
// pull each char from the String and show its position.
for ( x = 0; x < s.length(); x++ )
System.out.println("s.charAt(" + x + ") = " + s.charAt(x));
// Create the reverse building a string working
// from the end back toward the beginning.
rev = "";
for ( x = s.length() - 1; x >= 0; x-- )
rev = rev + s.charAt(x);
// print both original and reverse.
System.out.println("\n" + s);
System.out.println(rev);
}
}
Example 2c: Using a for
loop to display characters and to reverse a String
.
- The first
for
loop displays the individual characters of theString
. - Then we set
rev
to the empty string. - The next
for
loop picks characters from the end of the original and adds them, in order, onto the new string.
The output of Example 2c is shown below.
s.charAt(0) = F s.charAt(1) = o s.charAt(2) = x s.charAt(3) = s.charAt(4) = o s.charAt(5) = n s.charAt(6) = s.charAt(7) = t s.charAt(8) = h s.charAt(9) = e s.charAt(10) = s.charAt(11) = l s.charAt(12) = a s.charAt(13) = w s.charAt(14) = n s.charAt(15) = . Fox on the lawn. .nwal eht no xoF
The do-while
Loop
The do-while
loop, often called the do
loop, is a bit different than the previous loop constructs. This one is a bottom-test loop. Recall from the overview that this loop also differs from the other two in that we will always perform the work in the loop's body once before applying the condition test.
The best example of the necessity of this type of loop is the menu selection program. We know we must display the menu choices once and read the user's menu selection before we decide anything else.
We could decide that the user has chosen to quit the program through an appropriate menu choice. Another decision could be to re-display the menu repeatedly until they make a valid selection. Let's look at the program in Example 3.
import java.util.Scanner;
public class Menu {
static Scanner kb = new Scanner(System.in);
public static void main(String[] args) {
char choice;
boolean valid;
valid = false;
do {
System.out.println("\nPlease select a menu option.\n");
// show menu choices.
System.out.println("Choices:");
System.out.println("\tC) County Maps");
System.out.println("\tM) Municipalities");
System.out.println("\tT) Thermal Images");
System.out.println("\tU) Utilities");
System.out.println("\n\tQ) Quit");
// accept choice
System.out.print("\n\nChoice: ");
choice = Character.toUpperCase(kb.next().charAt(0));
// decide what to do with the selection.
switch (choice) {
// combined cases.
case 'C', 'M', 'T', 'U', 'Q':
valid = true;
break;
default:
System.out.println("The choice \"" + choice + "\" is not valid.");
break;
}
} while (!valid);
}
}
Example 3: Menu program using a do-while
loop.
You should first notice the use of the valid
variable. The fact that it is boolean
may trigger something you saw with while
. This turns our do-while
loop into a flag-based loop. Since there is a significant amount of processing needed to determine the choice made and whether it is a valid choice, a switch
statement has been added to simplify the use of a conditional statement to determine the validity of the value of choice
.
The default
clause of the switch
statement handles error reporting. All the case
entries have been lumped together to treat them equally as valid choices. If additional processing were needed beyond simple acceptance, handling it in another piece of code would likely be more appropriate. Remember that this is to identify the correct menu choice. Keeping the loop focused is essential.
A rather interesting concept to point out has to do with the reading and normalizing of the input data in the following statement:
choice = Character.toUpperCase(kb.next().charAt(0));
We read the next character from the input stream and then convert the character to the upper case with this one line of code. Remember that kb.next().charAt(0)
is how we read a character from the Scanner
object, and although it consumes a whole word, it is an acceptable sacrifice. It also means that we wait specifically for a return key to be pressed.
The reading of the character is nested inside a call to the method toUpperCase()
from the Character
wrapper class. This form of code writing is not uncommon and encouraged where appropriate.
When reading this line of code, begin on the right-hand side of the assignment and work the parentheses from the inside out. Read it a few times and say aloud what you believe is happening, then check with the possibilities below. There are several ways to express how this works.
kb
returns achar
which is converted to upper case and assigned tochoice
.choice
gets the value of the next character read fromkb
converted to upper case.
By converting the character to upper case (or lower case if that is preferred), we do two things:
- Set the stage to accept either upper or lower-case choices since they will be converted.
- If accepting both character cases is acceptable, we reduce the list of potential values by one-half since we will never have to test the lowercase possibilities.
The loop will prompt the user with the complete menu and prompt for a choice as long as they continue to make invalid selections.
The break
and continue
statements
We have already seen the use of break
in the switch statement
. It was used to leave the switch and continue to the following statement. This was done because the switch is a bunch of different case
scenarios. Generally, there was only one we were interested in, although case
statements can be combined. Since we met the case we were looking for, we break
out of the switch
.
When dealing with loops, there are times that we wish we could either proceed to the next iteration or stop the loop immediately. We do not want to stop the program, just the loop we may be stuck in or otherwise no longer desire to be a part of.
The break
and continue
reserved words are used as complete statements. These statements allow us to do exactly what their names suggest:
break
out of a loop or skip the remainder of aswitch
statement.continue
immediately with the next iteration of the loop.
The break
statement causes program execution to proceed to the statement after the end of the current compound statement block.
The continue
statement causes the loop to proceed to the next iteration by forcing the loop to move to the condition test. For a while
or do-while
loop this means proceeding to the condition test. For the for
loop, the update portion is done first, then the loop proceeds to the condition test.
Here is an example of break
with a swtich
. It checks to see if a String is made up of hexadecimal digits. At first sight of an invalid digit, it leaves the loop.
hex = true;
for (x = 0; x < t.length(); x++)
if ( hdigits.indexOf(t.charAt(x)) == -1 ) {
hex = false;
break;
}
System.out.println("Hex check 1 says " + hex);
Below is an example of how continue
can handle bad input and force the loop to its next iteration.
do {
// ...
System.out.println("\nEnter your guess:");
input = kb.nextLine().toUpperCase().replace(" ", "");
if ( ! input.matches("^[ROYGBW]{4}$") ) {
System.out.println(input + " is an invalid guess. Please retry!\n");
continue;
}
//...
} while (guess < maxGuesses);
Nested Loops
Placing a loop inside another loop allows for the complete set of inner loop iterations for each iteration of the outer loop. Nested loops are often used to process two- or three-dimensional data, but they are not limited to those applications.
public class Primes {
public static void main(String[] args) {
int number, range;
boolean prime;
// looking for primes between 2 and 100.
for ( number = 2; number <= 100; number++) {
// dividible by 2?
if ( number % 2 != 0 )
prime = true;
else
prime = false;
// divisible by other odd values?
for ( range = 3; prime && range < number; range = range + 2 ) {
if ( number % range == 0 )
prime = false;
}
if ( prime )
System.out.println(number);
}
}
}
Example 4a: Nested loop to find prime numbers from 2 to 100.
Consider Example 4a, which finds all the prime numbers between 2 and 100. We will use the outer loop to process the actual numbers 2 through 100, and the inner loop will be used to find out which odd values in the range from 3 to the number-1
are evenly divided into number
.
The code in Example 4a shows how the two loops determine if the number is prime. Remember that a number is prime if divisible only by itself and 1. We assume that number
is prime until we find a value that divides evenly into it. The values of range
are simply the set of numbers we will test with.
Although the compound statement is not required for the inner loop, it's been included for clarity. Before entering the inner loop, we also eliminate all even numbers with the if test.
Finally, we are using short-circuit evaluation for the inner for
loop. When prime
is false
, the loop is not entered. However, because the compiler knows that if the left operand to &&
is false
, there is no point in evaluating the right operand.
A slightly more efficient version is included in Example 4b, whereby we restructure the outer loop to only consider odd numbers, eliminating the even test.
public class PrimesImproved {
public static void main(String[] args) {
int number, range;
boolean prime;
// looking at only odd numbers
for ( number = 3; number <= 97; number = number + 2) {
// always assume it's prime.
prime = true;
// divisible by other odd values?
for ( range = 3; prime && range < number; range = range + 2 ) {
if ( number % range == 0 )
prime = false;
}
if ( prime )
System.out.println(number);
}
}
}
Example 4b: Nested loop to find prime numbers from 2 to 100.
File Handling
Ultimately, you must handle data that is too large to type in. File handling is essential to successful programming practice.
As a programmer, you should know what files are and that their names and extensions mean many things, including the type of data, its unique layout, and the encoding of its content.
Now, a favorite yet short-lived television show (Firefly) yielded a movie (Serenity) and a plethora of great lines, which are presented below to represent our data file for the following few examples.
Now I did a job. I got nothing but trouble since I did it, not to mention more than a few unkind words as regard my character so let me make this abundantly clear. I do the job. And then I get paid. You have reputation! Malcolm Reynolds gets it done, is the talk. You know what is reputation? Is people talking, is gossip. I also have reputation; not so pleasant, I think you know. Crow! Now for you, my reputation is not from gossip. You see this man? Ehh, he does not do the job. I show you what I do with him, and now for you, my reputation is fact. Is solid. You do the train job for me; then you are solid. You do not like I kill this man? My wife’s nephew. At dinner, I am getting earful. There is no way out of that. Anything goes wrong, then your reputation only gossip, and things between us not so solid. Yes? Sure, I got a secret. More'n one. Don't seem likely I'd tell 'em to you, do it? Anyone off Dyton Colony knows better'n to talk to strangers. You're talking loud enough for the both of us, though, ain't you? I've known a dozen like you. Skipped off home early, minor graft jobs here and there. Spent some time in the lock-down, I warrant, but less than you claim. Now you're what, petty thief with delusions standing? Sad little king of a sad little hill. Listen, you don’t know me, son, so I’m gonna say this once: if I ever kill you, you’ll be awake, you’ll be facing me, and you’ll be armed. Dear Diary, Today I was pompous, and my sister was crazy. Today we were kidnapped by hill folk, never to be seen again. It was the best day ever. Y'all see the man hanging out of the spaceship with the really big gun? Now I'm not saying you weren't easy to find. It was kinda out of our way, and he didn't want to come in the first place. Man's lookin' to kill some folk. So really, it's his will y'all should worry about thwarting.
This content should be copied and pasted into quotes.txt
file. This file will be referenced in the next few examples, as noted previously, and should be located in the same directory as the example programs.
Recall the Scanner
class can take an argument like System.in
to help associate a reference variable to the standard input object. We can also associate a filename to the Scanner
class by using another class called FileReader
. The FileReader
constructor takes a filename represented as a String
literal for its argument and returns an object suitable for Scanner
.
The first piece of code presented will simply open the file and print all the lines to the screen. This program is outlined in Example 5a. Using the Scanner wrapped around the FileReader
, we can read lines, words, integers - whatever is necessary to collect from the input file. In this case, we are sticking with reading lines.
import java.io.FileReader;
import java.util.Scanner;
import java.io.FileNotFoundException;
public class PrintLines {
public static void main(String[] args) throws FileNotFoundException {
String line;
// Wrap scanner around the FileReader that opens the file.
Scanner inFile = new Scanner(new FileReader("/uploads/quotes.txt"));
// EOF loop
while ( inFile.hasNext() ) {
line = inFile.nextLine();
System.out.println(line);
}
inFile.close();
}
}
Example 5a: Program using data file and FileReader
class.
The java.io
package contains the FileReader
class, and, as shown in Example 5b
, the PrintWriter
class. What is wonderful about the PrintWriter
class is that our outFile
reference variable utilizes the methods that we previously used with System.out
. So, a simple call to println()
allows us to write the content to outFile
that was just read from inFile
.
import java.io.FileReader;
import java.util.Scanner;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
public class CopyLines {
public static void main(String[] args) throws FileNotFoundException {
String line;
// setup input and output files.
Scanner inFile = new Scanner(new FileReader("/uploads/quotes.txt"));
PrintWriter outFile = new PrintWriter("/myfiles/copy.txt");
// EOF loop to read from one file and write to the other.
while ( inFile.hasNext() ) {
line = inFile.nextLine();
outFile.println(line);
}
// close files. Output is NOT flushed without a close()!
outFile.close();
inFile.close();
}
}
Example 5b: Adding PrintWriter
class to produce a new file.
While copying contents from one file to another is beneficial, the next few examples will deal with how we can further process the contents of an input file. In Example 5c, we simply count the number of lines read from inFile
.
import java.io.FileReader;
import java.util.Scanner;
import java.io.FileNotFoundException;
public class CountLines {
public static void main(String[] args) throws FileNotFoundException {
String line;
int count = 0;
Scanner inFile = new Scanner(new FileReader("/uploads/quotes.txt"));
// EOF loop to read and count lines.
while ( inFile.hasNext() ) {
line = inFile.nextLine();
count++;
}
inFile.close();
System.out.println("Number of lines is " + count);
}
}
Example 5c: Counting lines.
Now, the code in Example 5d takes the idea of Example 5c and adds to it the idea of counting words. However, in this version, we are adding a second Scanner
object to read the words from a String
. The Scanner
class has the ability to use a String as a form of input. So, the code creates a second Scanner
object, lineScanner
, and uses line
to feed the Scanner
.
import java.io.FileReader;
import java.util.Scanner;
import java.io.FileNotFoundException;
public class CountLinesAndWords {
public static void main(String[] args) throws FileNotFoundException {
String line, word;
int count = 0, words = 0, totalWords=0;
Scanner inFile = new Scanner(new FileReader("/uploads/quotes.txt")), lineScanner;
// EOF loop to read lines.
while ( inFile.hasNext() ) {
line = inFile.nextLine();
System.out.println(line);
count++;
// Scanner for the line to read words in the String.
lineScanner = new Scanner(line);
// for loop to count words as they're read from the line.
for (words = 0; lineScanner.hasNext(); words++) {
word = lineScanner.next();
System.out.println(word);
}
lineScanner.close();
System.out.println("Words in this line is " + words);
totalWords += words;
}
inFile.close();
System.out.println("Total number of lines is " + count);
System.out.println("Total number of words is " + totalWords);
}
}
Example 5d: Counting lines and words.
In Example 5d, we used the Scanner
class twice. This is an example of using the same tool to solve two problems. The first problem is reading lines from the file. The second is to read words from a line we read from the file.
- We use
infile
to prepare aScanner
object for reading lines from the file. - The
while
loop checks for more data in theScanner
. If true, it reads the next line of text intoline
. - We use
lineScanner
to prepare a secondScanner
object (lineScanner
) solely for capturing words from the line of text we read. - The
for
loop andlineScanner
are used to gather words fromline
.
This arrangement of loops and Scanner
objects allows us to gather more knowledge from the data. It is important to note that the for loop could have been written as a more traditional EOF loop. We could have just as easily written:
for (words = 0; lineScanner.hasNext(); words++) {
word = lineScanner.next();
System.out.println(word);
}
Original for
loop from Example 5d.
as
words = 0;
while (lineScanner.hasNext()) {
word = lineScanner.next();
words++;
System.out.println(word);
}
The for
loop rewritten as a while
loop.
String
method called split()
that allows you to do something quite similar to the code in Example 5d. However, the result is returned as an array of strings. As arrays are well beyond the scope of this chapter, they will be detailed in Chapter 9. Still, ArrayList
and basic use of arrays are shown from here to the end of this chapter to illustrate what you can do with them!import java.io.FileReader;
import java.util.Scanner;
import java.io.FileNotFoundException;
public class ArrangeWords {
public static void main(String[] args) throws FileNotFoundException {
String word;
int words = 0, lines = 0;
Scanner inFile = new Scanner(new FileReader("/uploads/quotes.txt"));
// EOF loop to read one word at a time.
while ( inFile.hasNext() ) {
word = inFile.next();
System.out.print(word + " ");
words++;
// have 5 words been printed?
if ( words > 0 && words % 5 == 0 ) {
System.out.println();
lines++;
// ok, have 5 lines been printed?
if (lines % 5 == 0)
System.out.println();
}
}
inFile.close();
}
}
Example 5e: Arranging words in 5x5 blocks.
The code in Example 5e has a few details worth mentioning.
- After printing
word
, we increment the number ofwords
read so far. - The outer
if
test determines if a newline should be printed based on the number ofwords
printed. If evenly divisible by 5, we print the newline and increment the number oflines
. - The inner
if
test determines if a newline should be printed based on the number oflines
printed. If evenly divisible by 5, we print an additional newline to create 5x5 boxes of words.
The sample output is shown below.
Now I did a job. I got nothing but trouble since I did it, not to mention more than a few unkind words as regard to my character so let me make this abundantly clear. I do the job. And then I get paid. You have reputation! Malcolm Reynolds gets ...
Finally, in Example 5f, we introduce the ArrayList
. While this is a bit outside the scope of the beginner realm, it is not difficult to understand the purpose of ArrayList
even if we do not completely understand how it works.
import java.io.FileReader;
import java.util.Scanner;
import java.util.ArrayList;
import java.io.FileNotFoundException;
public class ArrangeWords2 {
public static void main(String[] args) throws FileNotFoundException {
String word;
int count = 0, lines = 0, size;
ArrayList<String> allWords = new ArrayList<>();
Scanner inFile = new Scanner(new FileReader("/uploads/quotes.txt"));
// read each word and add to ArrayList
while ( inFile.hasNext() ) {
word = inFile.next();
allWords.add(word);
count++;
}
inFile.close();
System.out.println("Number of words is " + count);
// This prints all the words in the array list and the size.
System.out.println(allWords);
size = allWords.size();
System.out.println(size);
// iterate over the collected words.
for (int x = 0; x < size; x++) {
System.out.print(allWords.get(x) + " ");
// have 5 words been printed?
if ( x > 0 && x % 5 == 0 ) {
System.out.println();
lines++;
// ok, have 5 lines been printed?
if (lines % 5 == 0)
System.out.println();
}
}
}
}
Example 5f: Arranging words in 5x5 blocks from a container.
The ArrayList
object we create in allWords
is essentially a bag holding all the String
objects read with inFile
. This is the reason we use the nomenclature ArrayListArrayList
class will contain the type String. This is called a checked type to ensure that what we put into the ArrayList
is the appropriate type.
While we do not need the ArrayList
to accomplish the task (as seen in Example 5e), it does offer the possibility of getting the words from the file, closing the file and holding the word list for future use. This is the difference between short-lived and long-lived data. Having additional containers allows the freedom to move data from one inflexible medium (like a file) to another, which can be scanned and manipulated at will (like an ArrayList
).
Quiz
Building a Command Line Interface
We have more programming prowess than you might think at this point in the semester. Consider for a moment that you wanted to build a command-line interface. Something like what is shown below:
Welcome to my really awesome command line interface! Type 'help' at any time for assistance. (v. 1.0) cmd>
The detail above shows a simple welcome message and provides basic assistance and the version of the product. The final thing displayed is the command prompt itself, cmd>. Since this is truly nothing more than an EOF-based while
loop, let us begin with a simple setup like Iteration 1.
import java.util.Scanner;
public class Commands1 {
static Scanner kb = new Scanner(System.in);
public static void main(String[] args) {
String line;
// Display the prompt, loop until EOF.
System.out.print("cmd> ");
while (kb.hasNext()) {
line = kb.nextLine();
System.out.print("cmd> ");
}
}
}
Iteration 1: The framework for reading lines.
The setup is textbook. There is no real revelation in its design. Prompting before the loop and at the end of it, we read a line from a simple Scanner
object into the line
variable.
The next logical step is identifying the first word of the line
as the command. The other possibility is that the whole line
is the command, such as 'help
'.
import java.util.Scanner;
public class Commands2a {
static Scanner kb = new Scanner(System.in);
public static void main(String[] args) {
String line, cmd;
int space;
System.out.print("cmd> ");
while (kb.hasNext()) {
line = kb.nextLine();
// look for a space
space = line.indexOf(' ');
if ( space != -1 ) {
cmd = line.substring(0, space);
} else
cmd = line;
System.out.println("The command is: " + cmd);
System.out.print("cmd> ");
}
}
}
Iteration 2a: Added code to find the command.
In Iteration 2a, the if
test deals with the identification of the command. Search for the space between the command and the first argument. Failing that, the whole line must be the command.
import java.util.Scanner;
public class Commands2b {
static Scanner kb = new Scanner(System.in);
public static void main(String[] args) {
String line, cmd;
int space, start;
System.out.print("cmd> ");
line = kb.nextLine();
space = line.indexOf(' ');
// whole line is my command!
if ( space == -1 )
System.out.println(line);
// command plus arguments!
else {
System.out.println(line.substring(0,space));
start = space + 1;
space = line.indexOf(' ', start);
while (space != -1) {
System.out.println(line.substring(start, space));
start = space + 1;
space = line.indexOf(' ', start);
}
System.out.println(line.substring(start));
}
}
}
Iteration 2b: Looping to find the arguments.
Now we introduce a slightly more complex idea: the array. An array is simply a collection of like types in a single variable. While this topic is discussed later, the concepts can still be understood. We will now introduce the parts
variable, which is an array of String
.
import java.util.Scanner;
public class Commands3 {
static Scanner kb = new Scanner(System.in);
public static void main(String[] args) {
String line, cmd;
String[] parts;
System.out.print("cmd> ");
while (kb.hasNext()) {
line = kb.nextLine();
parts = line.split(" ");
System.out.println("The command is: " + parts[0]);
System.out.print("cmd> ");
}
}
}
Iteration 3: Check the command.
In Iteration 3, we introduce the use of the String
array along with the split()
method. The split()
method allows us to generate a list of words using a delimiter. The delimiter is the space character. It denotes the end of one token and the beginning of the next. For this scenario, tokens are the command and its respective arguments.
The parts
variable will hold the list of tokens that were found in line
.
Now, we will add some slightly more advanced topics to make the code more streamlined. The use of another String
array (String[]
), the Arrays
utility class and the ArrayList
class. The use of the ArrayList
allows for simpler coding since we can use the method contains()
.
import java.util.Scanner;
import java.util.Arrays;
import java.util.ArrayList;
public class Commands4 {
static Scanner kb = new Scanner(System.in);
static String[] cmds = { "copy", "delete", "storage", "volume", "vserver", "help" };
static ArrayList clist = new ArrayList(Arrays.asList(cmds));
public static void main(String[] args) {
String line, cmd;
int space;
String[] parts;
System.out.print("cmd> ");
while (kb.hasNext()) {
line = kb.nextLine();
space = line.indexOf(' ');
// treat consecutive spaces as one.
parts = line.split("[ ]+");
cmd = parts[0];
// Is this a valid command?
if ( clist.contains(cmd) ) {
System.out.println("The command is: " + cmd);
// Are there any arguments? What are they?
if ( parts.length > 1 ) {
for (int x = 1; x < parts.length; x++)
System.out.println("Argument " + x + ": " + parts[x]);
}
} else {
// Not a valid command.
System.out.println("Error: " + cmd + " is not a valid command.");
}
System.out.print("cmd> ");
}
}
}
Iteration 4: But wait, there is more...
The code in Iteration 4 offers much more detail for discussion. Line 8 provides our list of commands, while line 9 converts the array of strings into an ArrayList
object. The benefit of doing this is that we have the list of commands in the array, convert it to an ArrayList
, and gain the benefit of confirming the command is good with a single line of code at line 22. The contains()
method searches the ArrayList
for the command and returns a boolean regarding its level of success.
Lines 30-32 also provide a means of counting and revealing the arguments to the command. By simply looking at the length of the parts
array, we can build a loop to display all of the arguments for that command.
Let us begin by understanding what is needed to complete the work.
import java.util.Scanner;
import java.util.Arrays;
import java.util.ArrayList;
public class CommandsLong {
static Scanner kb = new Scanner(System.in);
public static void main(String[] args) {
String line, cmd;
int space;
System.out.println("Welcome to my really awesome command line interface!");
System.out.println("Type 'help' at any time for assistance. (v. 1.0)\n");
System.out.print("cmd> ");
while (kb.hasNext()) {
line = kb.nextLine();
// look for a space
space = line.indexOf(' ');
if ( space != -1 ) {
cmd = line.substring(0, space);
} else
cmd = line;
// Is this a valid command?
if ( cmd.equals("copy") ) {
System.out.println("Performing the work of copy...");
// do the copy command
} else if ( cmd.equals("delete") ) {
System.out.println("Performing the work of delete...");
// do the delete command
} else if ( cmd.equals("storage") ) {
System.out.println("Performing the work of storage...");
// do the storage command
} else if ( cmd.equals("volume") ) {
System.out.println("Performing the work of volume...");
// do the volume command
} else if ( cmd.equals("vserver") ) {
System.out.println("Performing the work of vserver...");
// do the vserver command
} else if ( cmd.equals("help") ) {
System.out.println("Performing the work of help...");
// do the help command
} else {
// Not a valid command.
System.err.println("Error: " + cmd + " is not a valid command.");
}
System.out.print("cmd> ");
}
}
}
Iteration 5: Getting back to basics.
We have removed all the flashy stuff in Iteration 5 and settled back into simply searching for a space. After that, we begin the process of identifying the command itself. This results in a multi-way if
test to compare all possible commands. The use of the compareTo()
method of the String
class provides the ability to check each command and then process the work for that command.
Remember that we cannot simply use the == operator with strings. We must use the compare methods the String
class provides.
Exercises
- (Beginner) Take the code in Example 1a and convert it to an EOF loop. Since the loop is now bounded by EOF, you will need to keep track of how many values are entered.
- (Intermediate) Write a loop to read four letter grades from the user. Using the letter grade
switch
statement from Chapter 4, write a program to calculate the GPA of a student's grades (for example A, B, C, C) where each class is 4 credits. [ GPA = ( quality_points * credits + quality_points * credits ... ) / total_credits ). - (Advanced) Perform the same GPA calculation as #2, but ask the user for the 4 grades and the number of credits for each grade (they can be different!).
- (Advanced) Write a loop to read strings from the user until EOF. With each string, determine how many letters and whitespace exist. Report these as separate values. [Hint: Using the
charAt()
method, pass eachchar
to one of theCharacter
class is-methods. This will be a nested loop - like afor
loop inside the EOF loop.]