// 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: * *
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); *
* * The 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: * *
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); *
**/ 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 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: * *
doubleInput, intInput *
doubleInputLine, intInputLine *
doubleQuery, intQuery *
**/ 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 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); } }