CSCI 1103 Lab12: Input with Scanner
- Due: 11:59pm Friday 11/27/2017
- Approximately 0.83% of total grade
- Submit to Canvas
- You may work with one partner on this lab but both partners must submit files and be physically present for Check-offs
- Lab exercises are open resource/open collaboration. You may freely discuss lab topics with other members of the class.
CODE DISTRIBUTION: lab12-code.zip
- Download the code distribution every lab
- See further setup instructions below
CHANGELOG:
Empty
Table of Contents
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 animport
statement - How to create a
Scanner
which reads from aString
and from aFile
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