package rebelsky.io; import java.io.BufferedReader; import java.io.EOFException; import java.io.IOException; import java.io.InputStreamReader; import java.text.NumberFormat; import java.text.ParsePosition; /** * A simple input system for beginning programmers. Permits reading * of basic data types without delving into the workings of the * various Java libraries. Does not throw exceptions. *

* If something goes wrong with one of the read methods, an error * value is returned. You can determine what that value is with * getErrorXXX (e.g., getErrorInt) and * set the value with setErrorXXX). You can also use * checkError to determine whether a value you received * is intended to mark an error. *

* Copyright (c) 1998 Samuel A. Rebelsky. All rights reserved. * * @author Samuel A. Rebelsky * @version 1.1 of January 1998 */ public class SimpleInput { // +-------+------------------------------------------------------------- // | Notes | // +-------+ /* The current line of text is kept in a buffer (called line) and we do all of the work within that buffer. This makes it easier to do unreading, peeking, error recovery, and other similar manipulations. It also allows us to take advantage of the NumberFormat.parse() routine which can be used to parse numbers. We use a variable to keep track of whether or not we're at the end of the file. When readLine() throws an exception, we mark the end of the file. */ // +--------+------------------------------------------------------------ // | Fields | // +--------+ /** * Are we testing this puppy? */ private static final boolean TESTING = false; /** * The base input. */ protected BufferedReader base; /** * Do we need to close our input? */ protected boolean needs_closing; /** * The current line. */ protected String buffer; /** * The position on the current line. */ protected ParsePosition pos; /** * A tool for parsing real numbers (floats and doubles). */ protected NumberFormat real_parser; /** * A tool for parsing integers (and longs). */ protected NumberFormat int_parser; /** * Are we at the end of the file? */ protected boolean at_eof; /** * The default double value used when errors occur. */ protected double DOUBLE_ERROR = Double.NaN; /** * The string used when errors occur. */ protected String STRING_ERROR = ""; /** * The default integer value used when errors occur. */ protected int INT_ERROR = -9999; /** * The default character value used when errors occur. */ protected char CHAR_ERROR = (char) -1; /** * Did we just encounter an error? */ protected boolean error = false; // +--------------+------------------------------------------------------ // | Constructors | // +--------------+ /** * Create a new object to read from standard input. *
pre: standard input has not been modified. *
post: the object will read from standard input. */ public SimpleInput() { base = new BufferedReader(new InputStreamReader(System.in)); real_parser = NumberFormat.getNumberInstance(); int_parser = NumberFormat.getNumberInstance(); int_parser.setParseIntegerOnly(true); needs_closing = false; buffer = null; pos = null; at_eof = false; } // SimpleInput // +------------------+-------------------------------------------------- // | Exported Methods | // +------------------+ /** * See if the last input operation we did returned an error. *
pre: We've completed an input operation. *
post: Returns true if the last operation failed. * Returns false otherwise. */ public boolean checkError() { return error; } // checkError /** * See if we're at the end of the file. *
pre: (none) *
post: if no input has been read, input may be read *
post: returns true if we've reached the end of the file */ public boolean eof() { // Make sure that the buffer is full. fillBuffer(); // See if we've hit the end of file. return at_eof; } // eof /** * See if we're at the end of the line. *
pre: (none) *
post: if no input has been read, input may be read *
post: returns true if we're currently at the end of the line */ public boolean eoln() { // Make sure that the buffer is full fillBuffer(); // If the index is off of the string, then we're at the end of line. return (pos.getIndex() >= buffer.length()); } // eoln() /** * Get the double used to report errors. *
pre: (none) *
post: The double currently used to report errors is returned. */ public double getErrorDouble() { return DOUBLE_ERROR; } // getErrorDouble /** * Get the integer used to report errors. *
pre: (none) *
post: The integer currently used to report errors is returned. */ public int getErrorInt() { return INT_ERROR; } // getErrorInt /** * Get the string used to report errors. *
pre: (none) *
post: The string currently used to report errors is returned. */ public String getErrorString() { return STRING_ERROR; } // getErrorString /** * Look at the next character in the buffer. *
pre: we haven't reached the end of the buffer. *
post: the next character to be read is returned. *
post: one line of input may be read. */ public char peek() { // Make sure there's stuff in the buffer fillBuffer(); // Are we at the end of file? If so, return an error. if (at_eof) { return CHAR_ERROR; } // Are we at the end of the line? If so, return the end-of-line // character (currently Unix-specific). else if (eoln()) { return '\n'; } // Return the appropriate character return buffer.charAt(pos.getIndex()); } // peek /** * Read the next character (including whitespace) from the given * input. If you are at the end of the line, returns the end * of line character. *
pre: (none) *
post: input may be read */ public char readChar() { // Get the current character char ch = peek(); // Advance the position pos.setIndex(pos.getIndex()+1); // Return the character return ch; } // readChar() /** * Read a double from the given input. Skips any whitespace up * to the double . If the next thing in the input is not a double * sets the cursor at the beginning of that thing, but does not * read beyond it and returns the default double. *
pre: the next input characters are whitespace and those * that make up an double. *
post: the cursor is after those digits. */ public double readDouble() { // Assume no errors. error = false; // Make sure the buffer is full fillBuffer(); // If we're at the end of the file, report an error if (at_eof) { error = true; return INT_ERROR; } // Skip any whitespace skipWhite(); // Get and return the next integer try { return real_parser.parse(buffer, pos).doubleValue(); } catch (Exception e) { error = true; return DOUBLE_ERROR; } // exception } // readDouble() /** * Read an integer from the given input. Skips any whitespace up * to the integer. If the next thing in the input is not an integer, * sets the cursor at the beginning of that thing, but does not * read beyond it and returns the default integer. *
pre: the next input characters are whitespace and those * that make up an integer (optional sign and some number * of digits). *
post: the cursor is after those digits. */ public int readInt() { // Assume no errors. error = false; // Make sure the buffer is full fillBuffer(); // If we're at the end of the file, report an error if (at_eof) { error = true; return INT_ERROR; } // Skip any whitespace skipWhite(); // Get and return the next integer try { return int_parser.parse(buffer, pos).intValue(); } catch (Exception e) { error = true; return INT_ERROR; } // exception } // readInt /** * Read a long integer from the given input. Skips any whitespace up * to the integer. If the next thing in the input is not an integer, * sets the cursor at the beginning of that thing, but does not * read beyond it and returns the default integer. *
pre: the next input characters are whitespace and those * that make up an integer (optional sign and some number * of digits). *
post: the cursor is after those digits. */ public long readLong() { // Assume no errors. error = false; // Make sure the buffer is full fillBuffer(); // If we're at the end of the file, report an error if (at_eof) { error = true; return INT_ERROR; } // Skip any whitespace skipWhite(); // Get and return the next integer try { return int_parser.parse(buffer, pos).longValue(); } catch (Exception e) { error = true; return INT_ERROR; } // exception } // readLong /** * Read one line from the given input and return that line without * any carriage return or other line-terminating character. If * we're at the end of file, return the empty string. *
pre: we haven't reached the end of the input. *
post: the next line (or partial line, if we're already on a * line) is returned. */ public String readLine() { // Assume no errors error = false; // Make sure the buffer is full fillBuffer(); // If we're at the end of the file, return the error string if ((at_eof) || (buffer == null)) { error = true; return STRING_ERROR; } // Get the rest of the current line String rest = buffer.substring(pos.getIndex()); // Clear the buffer in preparation for reading future lines. clearBuffer(); // Return the appropriate string return rest; } // readLine /** * Read a double from the current line and then skip to the end * of the line. *
pre: Not yet at end of file. *
pre: The next thing in the input is a double. *
post: who knows */ public double readLineDouble() { double tmp = readDouble(); readLine(); return tmp; } // readLineDouble() /** * Read the next nonempty string, skipping any intervening whitespace * (including carriage returns). *
pre: (none) *
post: The next nonempty string is returned. */ public String readString() { // The starting position of string on this line int start; // The ending position of the string on this line int end; // Assume no errors error = false; // Make sure the buffer is full fillBuffer(); // Skip over whitespace skipWhite(); // If we're at the end of the file, return the error string if ((at_eof) || (buffer == null)) { error = true; return STRING_ERROR; } // Determine the starting point start = pos.getIndex(); // Keep reading until we hit the end of the line or reach a // space. while (!eoln() && !Character.isWhitespace(buffer.charAt(pos.getIndex()))) { // Advance the position pos.setIndex(pos.getIndex() + 1); } // while // Back up one end = pos.getIndex(); // Return the appropriate substring return buffer.substring(start,end); } // readString /** * Set the double used to report errors. *
pre: (none) *
post: the parameter is returned whenever a double should * be returned and an error occurs. */ public void setErrorDouble(double error) { DOUBLE_ERROR = error; } // setErrorInt /** * Set the integer used to report errors. *
pre: (none) *
post: the parameter is returned whenever an integer should * be returned and an error occurs. */ public void setErrorInt(int error) { INT_ERROR = error; } // setErrorInt /** * Set the error string. *
pre: (none) *
post: the error string is now equal to the parameter. */ public void setErrorString(String error) { STRING_ERROR = error; } // setErrorString /** * Skip over any whitespace until we reach a non-whitespace * character or the end of file. *
pre: none *
post: the cursor is at the next non-whitespace character * of we've reached the end of file. */ public void skipWhite() { // Keep going until we hit a character or end of file. The // exit for a character happens within the while loop. while(!eof()) { // If we're at the end of the line, refill the buffer if (eoln()) { clearBuffer(); fillBuffer(); } // Otherwise, check the next character else { // If it's not whitespace, we're done if (!Character.isWhitespace(peek())) { return; } // Otherwise, advance in the buffer else { pos.setIndex(1+pos.getIndex()); } } // check the next character } // while } // skipWhite() // +-----------------+--------------------------------------------------- // | Local Utilities | // +-----------------+ /** * Make a comment. */ protected void COMMENT(String note) { if (TESTING) { System.err.println("*** " + note); } } // COMMENT /** * Make sure the buffer is set. *
pre: (none) *
post: buffer is non-null *
post: pos is set to 0 */ protected void fillBuffer() { // If the buffer is null, then it needs to be filled. if ((!at_eof) && (buffer == null)) { // Get the next line, obersving end of files try { buffer = base.readLine(); } catch (EOFException e) { at_eof = true; } // catch catch (IOException e) { // Some error, ignore it COMMENT("Caught the exception " + e.toString()); } // Update the position pos = new ParsePosition(0); // If we got a null, then we're at the end of the file (I would // have thought an error would be thrown, but ...) if (buffer == null) { at_eof = true; } } } // fillBuffer /** * Clear the buffer. *
pre: (none) *
post: buffer is null *
post: pos is null */ protected void clearBuffer() { buffer = null; pos = null; } // clearBuffer } // SimpleInput