/* dgkio.h CIS 150 7/1/05 David G. Klick Utility functions to make I/O easier and more robust for some common tasks: 1. getInt: gets an int value from the user with validation and minimum and maximum checking 2. getDouble: gets a double value from the user with validation and minimum and maximum checking 3. getChoice: gets a single character response from the user (with validation) for use with menu processing 4. processMenu: a more complete menu processing function which displays a menu and then calls getChoice See the more complete function descriptions included with each function definition in this file. */ #ifndef __DGKIO_H_ #define __DGKIO_H_ #include #include using std::cout; using std::cin; namespace dgk { /* This function takes a C-style string as input and returns true if it represents a valid integer and false if it doesn't. A single leading '+' or '-' is allowed. */ bool dgk::isValidInt(char* ptr) { char ch; int n = 0; // make sure we got a real pointer if (!ptr) return false; // get first character and make sure it exists ch = *ptr++; if (ch == '\0') return false; // allow a leading + or - if (ch=='+' || ch=='-') ch = *ptr++; // make sure all other characters are digits while (ch) { if (ch<'0' || ch>'9') return false; n++; // count number of valid digits ch = *ptr++; } // invalid if no digits present if (n == 0) return false; // if we got here, the number is OK return true; } /* This function takes a C-style string as input and returns true if it represents a valid float and false if it doesn't. A single leading '+' or '-' is allowed. A single decimal point is also allowed. */ bool dgk::isValidFloat(char* ptr) { char ch; int n = 0; bool hasDecimal = false; // make sure we got a real pointer if (!ptr) return false; // get first character and make sure it exists ch = *ptr++; if (ch == '\0') return false; // allow a leading + or - if (ch=='+' || ch=='-') ch = *ptr++; // make sure all other characters are digits // (also allows a single decimal point) while (ch) { if (ch == '.') { if (hasDecimal) return false; else hasDecimal = true; } else { if (ch<'0' || ch>'9') return false; else n++; // count number of valid digits } ch = *ptr++; } // invalid if no digits present if (n == 0) return false; // if we got here, the number is OK return true; } /* Prompts user for integer, does data validation, and returns value entered. */ int dgk::getInt(char* prompt, int min, int max) { char buf[20]; int num; bool valid = false; do { cout << prompt; cin.getline(buf, 20, '\n'); if (!isValidInt(buf)) cout << "Error: Invalid integer\n"; else { num = atoi(buf); if (num < min) cout << "Error: Value below minimum allowed\n"; else if (num > max) cout << "Error: Value above maximum allowed\n"; else valid = true; } } while (!valid); return num; } /* Prompts user for number, does data validation, and returns value entered. */ double dgk::getDouble(char* prompt, double min, double max) { char buf[20]; double num; bool valid = false; do { cout << prompt; cin.getline(buf, 20, '\n'); if (!isValidFloat(buf)) cout << "Error: Invalid number\n"; else { num = atof(buf); if (num < min) cout << "Error: Value below minimum allowed\n"; else if (num > max) cout << "Error: Value above maximum allowed\n"; else valid = true; } } while (!valid); return num; } /* Prompts user for menu choice, does data validation, and returns a valid entered value. A third boolean argument, which is optional determines whether all letters input will be treated as uppercase. The default is to leave input as-is. Setting the third argument to true will do the uppercase conversion of letters. */ char dgk::getChoice(char* prompt, char* allowed, bool upper = false) { char ans; char *pos; do { // prompt user for input and get user choice cout << prompt; cin >> ans; cin.ignore(); // get rid of newline // convert to uppercase if requested if (upper) ans = toupper(ans); // see if user input is in allowable response set pos = strchr(allowed, ans); // if input was invalid, display error message if (!pos) cout << "Error: Invalid choice\n"; } while (!pos); // continue looping if invalid response return ans; } /* This function displays a menu sent as a C-style string and then gets a one character menu choice from the user. It optionally converts that character to upper case if desired. The calling routine must provide C-style strings containing: 1. the menu itself 2. the user prompt 3. a string of allowable responses A fourth argument is optional. It is a boolean value which indicates whether user input that is letters should automatically be converted to uppercase. The default is to leave input as it is. */ char dgk::processMenu(char *menu, char* prompt, char* allowed, bool upper=false) { cout << menu; return getChoice(prompt, allowed, upper); } } // end of dgk namespace; #endif