// File: EasyReader.java from the package edu.colorado.io
// Complete documentation is available from the EasyReader link in:
// http://www.cs.colorado.edu/~main/docs
package edu.colorado.io;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PushbackReader;
/******************************************************************************
* The EasyReader
object has a small collection of methods for
* reading some primitive data values from an input stream or file.
*
*
Limitations:
*
* If an IOException
or FileNotFoundException
occurs
* during any operation, then the
* EasyReader
prints an error message and halts the program.
* The exceptions is not passed back to the calling program,
* so the calling program does not need to catch any exceptions.
*
*
Example:
*
* This example declares an EasyReader
that is attached to the
* keyboard input (System.in
). It then uses
* doubleQuery
to ask the user to enter a double number. The
* square of this double number is then printed:
*
*
*
* The
import edu.colorado.io.EasyReader
*
...
*
EasyReader stdin = new EasyReader(System.in); // Attaches to keyboard
*
double d;
*
d = stdin.doubleQuery("Please type a double value: ");
*
System.out.println("The square of that is: " + d*d);
* EasyReader
class includes:
*
* (1) Three constructors to create an EasyReader
from an
* InputStream
, from an InputStreamReader
, or from
* a file name. For example, to create an EasyReader
from
* System.in
:
* EasyReader stdin = new EasyReader(System.in);
*
* (2) Query methods: The names of these methods end with "Query".
* Each method prints a prompt (which is a String parameter) and then reads
* one line of input, converting the line to some type (char, double,
* int, or String). All of the query methods reject improperly formatted
* input lines (such as a 1.5 for an integer input). When an input line is
* rejected, the method prompts the user for a correctly formatted input.
* This continues until an input line is typed in the correct format.
*
* (3) Peek method: This method returns the next character of the input
* without actually reading it.
*
* (4) Input methods: The names of these methods end with "Input" or
* "InputLine". The methods read input and convert it to some type
* (char, double, int, or String). The "Input" methods just read the
* data, and the "InputLine" methods read the data and then discard the rest
* of the line.
*
* (5) Boolean methods: The names of these methods begin with "is". They
* return various information about the input status.
*
* (6) The methods skipLine and ignore. They
* read and throw away various input.
*
* Java Source Code for this class:
*
* http://www.cs.colorado.edu/~main/edu/colorado/io/EasyReader.java
*
*
* @author
* Michael Main (main@colorado.edu)
*
* @version Feb 10, 2016
*
* @see FormatWriter
******************************************************************************/
public class EasyReader extends PushbackReader
{
final static int EOF_VALUE = -1; // Returned by read( ) at EOF
final static char ZERO_CHAR = '\0'; // Unicode character zero
private boolean formatProblem = false;
/**
* Initialize this EasyReader
so that it reads from an
* InputStream
.
* @param in
* an InputStream
that this EasyReader
* will read from
* Postcondition:
* This EasyReader
has been initialized so that its
* subsequent input comes from the specified InputStream
.
* Example:
* EasyReader stdin = new EasyReader(System.in);
**/
public EasyReader(InputStream in)
{
super(new InputStreamReader(in));
}
/**
* Initialize this EasyReader
so that it reads from a
* specified file.
* @param name
* the name of the file that this EasyReader
* will read from
* Postcondition:
* This EasyReader
has been initialized so that its
* subsequent input comes from the specified file.
* If the file does not exist, then an error message is printed
* to System.err and the program exits.
* Example:
* EasyReader stdin = new EasyReader("foo.txt");
**/
public EasyReader(String name)
{
super(makeFileReader(name));
}
/**
* Initialize this EasyReader
so that it reads from an
* InputStreamReader
.
* @param isr
* an InputStreamReader
that this EasyReader
* will read from
* Postcondition:
* This EasyReader
has been initialized so that its subsequent
* input comes from the specified InputStreamReader
.
**/
public EasyReader(InputStreamReader isr)
{
super(isr);
}
/**
* Read a character from this EasyReader
.
* @return
* a character that's been read
* Note:
* This method reads and throws away whitespace. Then it reads and
* returns the next character. If end-of-file has been reached, then
* it returns ASCII value zero.
**/
public char charInput( )
{
readSpaces( );
return readChar( );
}
/**
* Read a character from a complete line of this EasyReader
.
* @return
* a character that's been read
* Note:
* This method is indentical charInput()
with an added
* activation of skipLine()
just before returning.
**/
public char charInputLine( )
{
char answer = charInput( );
skipLine( );
return answer;
}
/**
* Print a prompt, then read and return a character from this
* EasyReader
.
* @param prompt
* a prompt to print
* @return
* The prompt has been printed to System.out
. Then a
* character has been read and returned with charInputLine
.
**/
public char charQuery(String prompt)
{
char answer;
System.out.print(prompt);
return charInputLine( );
}
/**
* Read a double
number from this EasyReader
.
* @return
* a double
number that's been read
* Input Method:
* An attempt is made to read the following items into a
* String
:
* (1) Zero or more whitespace characters (which are discarded);
* (2) An optional + or - sign.
* (3) A sequence of digits that form the integer part of the number.
* (4) If the next character is a decimal point, then it is read
* along with a sequence of digits that form the fractional part
* of the number.
* (5) If the next character is 'e' or 'E', then it is read along
* with an optional +/- sign and digits that form the exponent
* part of the number.
* After these items, there may be a non-digit delimiter, or the
* end-of-file may appear after the number. The delimiter (or EOF)
* is not read.
* Conversion: The above items are converted to a double value
* using Double.valueOf
.
* Format Problems:
* If a NumberFormatException
* occurs, then the method returns Double.NaN
and an immediate
* activation of isFormatProblem()
will return true.
**/
public double doubleInput( )
{
final char POINT = '.';
StringBuffer input = new StringBuffer( );
readSpaces( );
input.append(readSign( ));
input.append(readDigits( ));
if (peek( ) == POINT)
{ // Read the decimal point and fractional part.
input.append(readChar( ));
input.append(readDigits( ));
}
if (Character.toUpperCase(peek( )) == 'E')
{ // Read the E and exponent.
input.append(readChar( ));
input.append(readSign( ));
input.append(readDigits( ));
}
try
{
formatProblem = false;
return Double.valueOf(input.toString( )).doubleValue( );
}
catch (NumberFormatException e)
{
formatProblem = true;
return Double.NaN;
}
}
/**
* Read a double value from a complete line of this EasyReader
.
* @return
* a double value that's been read
* Note:
* This method is identical doubleInput( )
with an added
* activation of skipLine( )
just before returning.
**/
public double doubleInputLine( )
{
double answer = doubleInput( );
skipLine( );
return answer;
}
/**
* Print a prompt, then read and return a double value from this
* EasyReader
.
* @param prompt
* a prompt to print
* @return
* The prompt has been printed to System.out
. Then a double
* value has been read and returned with doubleInputLine
.
* Format Problems:
* If doubleInputLine
encounters a format problem, but
* !isEOF()
, then the user is prompted to type a new
* input line until a correct double value is provided. If end-of-file
* is reached, then the method returns Double.NaN
and
* an immediate activation of isFormatProblem()
will return
* true.
**/
public double doubleQuery(String prompt)
{
double answer;
System.out.print(prompt);
answer = doubleInputLine( );
while (formatProblem)
{
System.out.print("Invalid response. Please type a double value: ");
if (isEOF( ))
return Double.NaN;
answer = doubleInputLine( );
}
return answer;
}
private static void handleException(Exception e)
// Print an error message and halt the program.
{
System.err.println("Exception:" + e);
System.err.println("EasyReader will cause program to halt.");
System.exit(0);
}
/**
* Read and discard one character.
* Postcondition:
* One character has been read and discarded.
**/
public void ignore( )
{
readChar( );
}
/**
* Read an integer from this EasyReader
.
* @return
* an integer that's been read
* Format:
* An attempt is made to read the following items into a
* String
:
* (1) Zero or more whitespace characters (which are discarded);
* (2) An optional + or - sign.
* (3) A sequence of digits that form the actual integer.
* There may be a non-digit delimiter after the integer, or the
* end-of-file may appear after the integer. The delimiter (or EOF)
* is not read.
* Conversion: The above items are converted to an integer value
* using Integer.parseInt
.
* Format Problems:
* If a NumberFormatException
* occurs, then the method returns Integer.MIN_VALUE
and an
* immediate activation of isFormatProblem()
will return true.
**/
public int intInput( )
{
String input = null;
readSpaces( );
input = readSign( ) + readDigits( );
try
{
formatProblem = false;
return Integer.parseInt(input);
}
catch (NumberFormatException e)
{
formatProblem = true;
return Integer.MIN_VALUE;
}
}
/**
* Read an integer from a complete line of this EasyReader
.
* @return
* an integer that's been read
* Note:
* This method is indentical intInput( )
with an added
* activation of skipLine( )
just before returning.
**/
public int intInputLine( )
{
int answer = intInput( );
skipLine( );
return answer;
}
/**
* Print a prompt, then read and return an integer from this
* EasyReader
.
* @param prompt
* a prompt to print
* @return
* The prompt has been printed to System.out
. Then an
* integer has been read and returned with intInputLine
.
* Format Problems:
* If intInputLine
encounters a format problem, but
* !isEOF()
, then the user is prompted to type a new
* input line until a correct int value is provided. If end-of-file
* is reached, then the method returns Integer.MIN_VALUE
* and an immediate activation of isFormatProblem()
will return
* true.
**/
public int intQuery(String prompt)
{
int answer;
System.out.print(prompt);
answer = intInputLine( );
while (formatProblem)
{
System.out.print("Invalid response. Please type an integer value: ");
if (isEOF( ))
return Integer.MIN_VALUE;
answer = intInputLine( );
}
return answer;
}
/**
* Determine whether this EasyReader
has reached the
* end-of-file.
* @return
* If this EasyReader
has reached the end of file
* (reading all characters up to but not including EOF), then the return
* value is true; if an attempt to read causes an IOException,
* then the return value is also
* true; otherwise the return value is false.
* Note:
* A user at the keyboard indicates EOF for standard input by typing
* ctrl-z (MS Windows) or ctrl-c (Unix). For an interactive user, this
* method does pause until the
* user provides some input or indicates end-of-file by pressing ctrl-z.
* Example:
* Read one integer per line from standard input until EOF, then print
* the sum of all the integers:
*
*
**/
public boolean isEOF( )
{
return (readAhead( ) == EOF_VALUE);
}
/**
* Determine whether the next input character is an end-of-line.
* @return
* If the next input character is a newline ('\n') or carriage return
* ('\r'), then the return value is true; if
EasyReader stdin = new EasyReader(System.in);
*
int sum = 0;
*
System.out.println("Type one int per line and press ctrl-z to end:");
*
while (!stdin.isEOF( ))
*
sum += stdin.intInputLine( );
*
System.out.println("Total sum: " + sum);
* isEOF()
, then the
* return value is also true; if an attempt to read causes an
* IOException
, then
* the return value is also true; otherwise the return value is false.
**/
public boolean isEOLN( )
{
int next = readAhead( );
return (next == '\n') || (next == '\r') || (next == EOF_VALUE);
}
/**
* Determine whether there was an incorrectly formatted input to the most
* recent input operation.
* @return
* A true return value indicates that the most recent activation of an
* input methods had an IOException OR was given input of the wrong form
* (such as "abc" instead of an integer). Note that the return value is
* applicable to only the MOST RECENT activation of these input methods:
*
*
**/
public boolean isFormatProblem( )
{
return formatProblem;
}
private static FileReader makeFileReader(String name)
// Create and return a FileReader to read from the named file. If the file doesn’t exist then print
// an error message and halt the program.
{
try
{
return new FileReader(name);
}
catch (FileNotFoundException e)
{
handleException(e);
return null;
}
}
/**
* Make the computation pause for a specified number of milliseconds.
* @param milliseconds
* the number of milliseconds to pause
* Postcondition:
* The computation has paused for the specified time.
**/
public static void pause(long milliseconds)
{
try
{
Thread.sleep(milliseconds);
}
catch (InterruptedException e)
{
// Resume execution
}
}
/**
* Peek ahead at the next character from this
doubleInput, intInput
*
doubleInputLine, intInputLine
*
doubleQuery, intQuery
* EasyReader
* (but don't read it).
* @return
* The return value is the next character that will be read from this
* EasyReader
. If there is no next character (because of
* the end-of-file marker), then the return value is '\0'.
**/
public char peek( )
{
int next = readAhead( );
if (next == EOF_VALUE)
return ZERO_CHAR;
else
return (char) next;
}
/**
* Print a prompt, then read and return a YES/NO answer from this
* EasyReader
.
* @param prompt
* a prompt to print
* @return
* stringQuery(prompt)
has been called to ask a question
* and read the answer, which is considered true if it begins with
* "Y" or "y" and false if it begins with "N" or "n". If the answer did
* not begin with a lower- or upper-case Y or N, then the process is
* repeated until a correct Yes/No answer is provided. If EOF is reached,
* then false is returned.
**/
public boolean query(String prompt)
{
String answer;
System.out.print(prompt + " [Y or N] ");
answer = stringInputLine( ).toUpperCase( );
while (!answer.startsWith("Y") && !answer.startsWith("N"))
{
System.out.print("Invalid response. Please type Y or N: ");
if (isEOF( ))
return false;
answer = stringInputLine( ).toUpperCase( );
}
return answer.startsWith("Y");
}
private int readAhead( )
// Peek ahead and return the next character (or -1 for EOF).
{
int next = EOF_VALUE;
try
{
next = read( );
if (next == EOF_VALUE)
{
// End-of-file was encountered. We pause 1 second to allow the
// ctrl-z from the keyboard to be processed since it blocks output
// to the screen on some systems.
pause(1000);
}
else
unread(next);
}
catch (IOException e)
{
handleException(e);
}
return next;
}
private char readChar( )
// Read and return the next character (or ZERO_CHAR for EOF).
{
int next = EOF_VALUE;
try
{
next = read( );
if (next == EOF_VALUE)
{
next = ZERO_CHAR;
// End-of-file was encountered. We pause 1 second to allow the
// ctrl-z from the keyboard to be processed since it blocks output
// to the screen on some systems.
pause(1000);
}
}
catch (IOException e)
{
handleException(e);
}
return (char) next;
}
private String readDigits( )
// Read a sequence of digits and return the sequence as a String.
{
StringBuffer buffer = new StringBuffer( );
while (Character.isDigit(peek( )))
buffer.append(readChar( ));
return buffer.toString( );
}
private String readSign( )
// Read a + or - sign (if one is present) and return the read characters as a string.
{
StringBuffer buffer = new StringBuffer(1);
char possibleSign;
possibleSign = peek( );
if ((possibleSign == '-') || (possibleSign == '+'))
buffer.append(readChar( ));
return buffer.toString( );
}
private String readSpaces( )
// Read a sequence of whitespace characters and return the sequence as a String.
{
StringBuffer buffer = new StringBuffer( );
while (Character.isWhitespace(peek( )))
buffer.append(readChar( ));
return buffer.toString( );
}
/**
* Read and discard the rest of the current input line.
* Postcondition:
* Characters have been read and discarded up to and including the end of
* the current input line (or to the end-of-file).
**/
public void skipLine( )
{
while (!isEOLN( ))
readChar( );
if (peek( ) == '\r')
readChar( );
if (peek( ) == '\n')
readChar( );
}
/**
* Read a String
(up to whitespace) from this
* EasyReader
.
* @return
* a String
that's been read
* Format:
* Whitespace has been skipped, and then a string has been read
* up to but not including the next whitespace character.
**/
public String stringInput( )
{
StringBuffer buffer = new StringBuffer( );
formatProblem = false;
readSpaces( );
while (!isEOF( ) && !Character.isWhitespace(peek( )))
buffer.append(readChar( ));
return buffer.toString( );
}
/**
* Read a String
from a complete line of this
* EasyReader
.
* @return
* a String
that's been read
* Format:
* An entire line of characters has been read up to and including the end
* of the current line (or the end-of-file). All characters before the
* end are returned in a String
.
**/
public String stringInputLine( )
{
StringBuffer buffer = new StringBuffer( );
while (!isEOLN( ) && !isEOF( ))
buffer.append(readChar( ));
skipLine( );
return buffer.toString( );
}
/**
* Print a prompt, then read and return a String
from this
* EasyReader
.
* @param prompt
* a prompt to print
* @return
* The prompt has been printed to System.out
. Then a
* String
has been read and returned with
* stringInputLine
.
**/
public String stringQuery(String prompt)
{
System.out.print(prompt);
return stringInputLine( );
}
/**
* A demonstration program.
* To run the demonstration:
* java edu.colorado.io.EasyReader
* @param args
* not used in this implementation
**/
public static void main(String[ ] args)
{
EasyReader stdin = new EasyReader(System.in);
double d = stdin.doubleQuery("Double: ");
if (stdin.isFormatProblem( ))
System.out.println("A format error resulted in " + d);
else
System.out.println(d + " is a fine double number.");
int i = stdin.intQuery("Int: ");
if (stdin.isFormatProblem( ))
System.out.println("A format error resulted in " + i);
else
System.out.println(i + " is a fine integer.");
String s = stdin.stringQuery("String: ");
if (stdin.isFormatProblem( ))
System.out.println("A format error resulting in " + s);
else
System.out.println('"' + s + '"' + " is a fine String.");
int sum = 0;
System.out.println("Type one int per line & press ctrl-z to end:");
while (!stdin.isEOF( ))
sum += stdin.intInputLine( );
System.out.println("Total sum: " + sum);
}
}