Last Updated: 2017-11-26 Sun 19:10

CSCI 1103 Lab12: Input with Scanner

CODE DISTRIBUTION: lab12-code.zip

  • Download the code distribution every lab
  • See further setup instructions below

CHANGELOG:

1 Rationale

Reading data from stored files is an essential skill. Reading from arbitrary sources such as the user, a string, or a network location usually follows a similar pattern to reading from files. Completing this lab will illustrate how Java's Scanner class provides a uniform mechanism to read from a variety of sources. To complete the lab, one must understand several key elements surrounding Scanner input:

  • How to notify the compiler that a Scanner will be used in your code by using an import statement
  • How to create a Scanner which reads from a String and from a File existing on disk
  • Which methods Scanner provides to read tokens from the input stream. For example, find out which method reads a single word as a string and which method reads a number as an integer.
  • Which methods Scanner provides to check if a particular type of token appears next in the input stream, for example if the next token is a real number.
  • How to determine if there is any input left in the input stream.

Processing data also requires mastery of basic programming concepts such as iteration, conditional execution, and function declaration which will be reinforced here. Finally, this lab illustrates how one central function can facilitate a variety of tasks by writing short functions that pass the central function appropriate arguments.

Associated Reading

Material from Eck Ch 5 is covered in this lab. The stack data structure is discussed in Ch 9.3.1 but implemented there differently there than this lab: the text uses linked nodes rather than contiguous arrays. This technique will be discussed later in class.

2 Download Lab Code and Setup

As in Lab01, download the code pack linked at the top of the page. Unzip this which will create a folder and create your files in that folder.

File State Notes
PARTNERS.txt Edit Fill in your name and the name of your partner in this text file
KTests.java Testing Utility routines for all tests
junit-1103.jar Testing For testing, don't try to edit this one
Problem1Tests.java Testing Tests for problem 1
Problem2Tests.java Testing Tests for problem 2
Problem3Tests.java Testing Tests for problem 3
Problem4Tests.java Testing Tests for problem 4
wordsNumbers1.txt Data Sample file associated with problem 3
wordsNumbers2.txt Data Sample file associated with problem 3
names1.txt Data Sample file associated with problem 4
names2.txt Data Sample file associated with problem 4
ScannerPractice.java Create Methods for all problems

3 Class setup: ScannerPractice

Create a class called ScannerPractice in a file called ScannerPractice.java which you will need to create. This class will contain several methods which will be tested. You may want to copy the skeleton of the class (found below) into your file to get started but know that you'll need to add method bodies to get the skeleton to compile. You may add a main() method and use it for testing your own functions and additionally any other helper functions you might find useful. Just make sure that all the required methods are present.

DrJava's interactive loop is also useful for quick, by-hand testing. The examples below illustrate this.

The ScannerPractice class at a minimum must have the following class structure.

public class ScannerPractice{
// Contains some simple parsing functions for reading from Scanners,
// Strings, and Files.
//
// Don't forget to import the Scanner and File classes at the top of
// the file.

  public static double sumReals(Scanner input);
  // Accept a Scanner argument and read space-separated words from
  // it. If a word is a valid real number such as 0.1 or -3.14159 or
  // 87 then add the number onto a running total.  When there is no
  // more input available in the scanner, return the total.

  public static double sumReals(String parseString);
  // Perform the same computation as sumReals(Scanner) except parse
  // the given string for real numbers.  You will need to create a new
  // Scanner which reads from parameter String.  It may be useful to
  // use employ sumReals(Scanner) in this function.

  public static double sumRealsInFile(String filename) throws Exception;
  // Perform the same computation as sumReals(Scanner) except read
  // from the file named by the parameter filename.  The actual file
  // with the text should already be stored on disk. Some sample files
  // are stored in wordsNnumbers1.txt and wordsNnumbers2.txt.  This
  // function may throw an exception if the named file does not
  // exist. It may be useful to use the function sumReals(Scanner).

  public static String [] readNamesFromFile(String filename);
  // Read a names from a file and return an array of the names.  Names
  // are stored one per line but may contain multiple parts and spaces
  // in them such as the following:
  //
  // Luke Cage
  // Jessica Jones
  // Matt Murdock
  // Danny Iron Fist Rand
  // Elektra Nachios
  // Alexandra
  // Madame Gao
  // Wilson Grant Fisk
  // 
  // Use two passes through the file. Count lines first, then allocate
  // an array, then read lines into the array. Put all operations in a
  // try/catch block. If an exception results, throw a new runtime
  // exception.

}

While there are 4 problems in the lab, it should be no longer than previous labs. Problem 1 requires getting acquainted with the Scanner class but once this is done, Problems 2 and 3 are made very short by leveraging the method defined in Problem 1. Problem 4 builds takes a different tack as it requires reading through a file and returning its contents as an array of Strings.

4 Import Appropriate Classes

The ScannerPractice class uses some classes from the Java library which are not available by default. Instead they must be explicitly imported. Make sure to include the following at the top of your file.

import java.util.Scanner;
import java.io.File;

This will make the Scanner and File classes available in the rest of the class. Should errors along these lines appear:

ScannerPractice.java:15: error: cannot find symbol
  public static double sumReals(Scanner input){
                                ^
  symbol:   class Scanner
  location: class ScannerPractice

it is likely an import will fix them.

5 Problem 1: sumReals(Scanner)

public static double sumReals(Scanner input)

This method takes an already open Scanner and proceeds to read input words from it. If any input word happens to represent a valid real number, it is added to a running total. When no input remains in the Scanner, this running total is returned. If no words are valid real numbers then 0.0 should be returned. Some example uses in DrJava's interactive loop are shown below.

Welcome to DrJava.  
> import java.util.Scanner;

// All numbers
> String allNums = "1.23 4.56 7.89";
> Scanner in = new Scanner(allNums);
> double total = ScannerPractice.sumReals(in);
> total
13.68
 
// All words
> String allWords = "none of these are numbers";
> in = new Scanner(allWords);
> ScannerPractice.sumReals(in)
0.0

// Mixed words and real numbers
> String mixed = "number 1.0    two  12.3   stuffed turkey -0.99   five";
> in = new Scanner(mixed);
> ScannerPractice.sumReals(in)
12.31

Constraint: Do not use regular expressions to solve this problem. They are not needed and will only over-complicate a good solution. Instead, focus on the built-in ability of Scanner to recognize different kinds of input and distinguish when a number can be read next or if the next token is an arbitrary string.

6 Problem 2: sumReals(String)

public static double sumReals(String parseString)

Rather than take an already opened Scanner this method takes a String to parse. It should create a Scanner object that reads from the parameter String and then perform the same computation as sumReals(Scanner).

Note that this illustrates that Java methods may have the same name so long as they have different argument types or different numbers of arguments.

Some sample uses

> ScannerPractice.sumReals("here are some numbers... psyche!")
0.0
> ScannerPractice.sumReals("43110")
43110.0
> ScannerPractice.sumReals("4.3 110.5  0.12  3.14")
118.06
> ScannerPractice.sumReals("one 1.0 two 2.0 thre-and-a-half 3.5 and so on...  ")
6.5

Hint: It is tempting to copy and paste code for similar methods but clever programmers figure out how to use an already written function to solve a new problem by passing the old function appropriately constructed arguments. The best solutions to this problem are 1-2 lines long.

7 Problem 3: sumRealsInFile(String filename)

public static double sumRealsInFile(String filename) throws Exception

This function opens a pre-existing file on disk, reads it word by word and sums any valid real numbers that appear in it. Some example files are present with the code distribution called wordsNumbers1.txt and wordsNumbers2.txt. Examples of summing reals in these files are as follows.

> ScannerPractice.sumRealsInFile("wordsNumbers1.txt")
15.912099999999999
> ScannerPractice.sumRealsInFile("wordsNumbers2.txt")
-12.3

Hint: The best solutions to this problem are 1-2 lines long and make use of another function you wrote.

Notice that the type signature for the method includes throws Exception. In Java, some operations can go wrong which triggers an exception. Methods that can cause certain kinds of exceptions must "admit" this fact by declaring that they can throw some kind of exception. In this case, creating a Scanner that reads from a file may encounter difficulties. For instance, if the requested file is not present, then an exception will be raised. For now, we will simply state an arbitrary Exception may be thrown but in the near future we will write more refined java which declares the specific kinds of exceptions that can be thrown, in this case a FileNotFoundException.

Note - only "checked" exceptions must have these throws annotations added; RuntimeException and Error types and their descendents are unchecked, and all others are checked. Most exceptions you experience will be either a RuntimeException (unchecked) or an IOException (checked), as we see here.

8 Problem 4: readNamesFromFile(String filename)

public static String [] readNamesFromFile(String filename)

This method reads all names from a file and returns an array filled with the names. Files with names in them look like the following.

File names1.txt

Nicole
Tyler
Devon

File names2.txt

Luke Cage
Jessica Jones
Matt Murdock
Danny Iron Fist Rand
Elektra Nachios
Alexandra
Madame Gao
Wilson Grant Fisk

Interactive demonstrations of the method are as follows.

Welcome to DrJava.
> String tas[] = ScannerPractice.readNamesFromFile("names1.txt");
> tas
{ Nicole, Tyler, Devon }
> tas.length
3
> String marvel[] = ScannerPractice.readNamesFromFile("names2.txt");
> marvel
{ Luke Cage, Jessica Jones, Matt Murdock, Danny Iron Fist Rand, Elektra Nachios, Alexandra, Madame Gao, Wilson Grant Fisk }
> marvel.length
8

Notice that names appear on a single line but may contain spaces such as Luke Cage or Wilson Grant Fisk. Also, notice that it is not known ahead of time how many names are in the file. Finally, notice that the type signature of the method does not involve any exceptions. All these problems must be surmounted to complete this problem.

8.1 Reading lines with a scanner

Since names do not follow a regular pattern and may include spaces, an appropriate method of Scanner should be used to read them. The nextLine() method is useful for this as it will read an entire line as a String starting from the current position. This is different from the next() method which will stop at any whitespace. Make use of nextLine() when dealing with names. It is useful to use accessor boolean hasNextLine() of Scanner which indicates whether the scanner has another line of text.

8.2 Two-Pass I/O: Count, Allocate, Read

To deal with the unknown number of names in a file, use one pass to count names/lines. This first pass does not save any data but instead just counts how many data items are present. After counting, allocate an array of the correct size. Re-initialize the Scanner to position it at the beginning of the file. Then use a second pass through the file to read data placing each data object into the array. The basic approach is as follows:

create a scanner INPUT reading from the file
initialize COUNT to 0

// count input size
while INPUT has more lines {
  read a line from INPUT but discard it
  increment COUNT
}

// allocate
create array DATA with size COUNT

// read
set INPUT to a new scanner whith positions it at the beginning of the file
for I=0 up to COUNT{
  read a line from INPUT
  store the line in DATA[I]
}

8.3 Catch and Re-throw

To avoid needing to declare that the method throws Exception, use a catch and re-throw approach. Exceptions can be caught and handled or new exceptions can be raised in their stead. There is no need to declare that unchecked exceptions like RuntimeException may be raised so the following basic template will do the trick.

public static String [] readNamesFromFile(String filename){
  try {
    // do the two pass I/O in this try block
   
  }
  catch(Exception e){              // if an exception results
    throw new RuntimeException(e); // re-throw it as a RuntimeException
  }
}

9 Getting Credit for this Lab

9.1 Demonstrate your code to Lab Staff (40%)

You should feel free at any time to get help from lab staff as you get stuck on the exercises.

By the end of the lab, make sure to demonstrate your code to a staff member. This will ensure that you receive full credit on the lab. Staff might ask you to

  • Change what is printed
  • Compile and run on the command line
  • Run tests in DrJava or on the command line
  • Show how to zip the folder with your programs in it

Be ready to do any or all of these.

9.2 Zip and Submit (60%)

Ensure that file PARTNERS.txt has your name and the name of your partner in it. This fill is located with the other Java files for the lab and can be edited with DrJava.

Once your are confident your code is working, you are ready to submit. Ensure your folder has all of the required files. Create a zip archive of your lab folder and submit it to blackboard.

On Canvas:

  • Click on the Assignments section
  • Click on the appropriate link for this lab
  • Scroll down to "Attach a File"
  • Click "Browse My Computer"
  • Select you Zip file and press OK

Author: Chris Kauffman (kauffman@cs.gmu.edu)
Date: 2017-11-26 Sun 19:10