/* utils150.h CIS 150 2008-06-17 David Klick Utility I/O functions used to make programming a little easier and safer. */ #ifndef __UTILS150_H_ #define __UTILS150_H_ #include #include #include #include using std::cout; using std::cin; using std::string; #define UTILS150_OK 0 #define UTILS150_ERR_INVALID_FORMAT 1 #define UTILS150_ERR_BELOW_MIN 2 #define UTILS150_ERR_ABOVE_MAX 3 namespace dgk { /* bool isValidInt(char* str); Arguments: char* str: the address of the C-style string to be checked Returns: true if ptr points to a string which can be interpreted as an integer, otherwise returns false 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. Leading whitespace is allowed. */ bool isValidInt(char* str) { if (str == NULL) return false; int slen = strlen(str); if (slen == 0) return false; char tmpStr[slen+1]; int tpos = 0; const int ST_INIT = 0; const int ST_SIGN = 1; const int ST_NUM = 2; const int ST_ERR = 3; int state = ST_INIT; char c; char* ptr = str; if (*ptr == '\0') return UTILS150_ERR_INVALID_FORMAT; while ((state != ST_ERR) && (c = *ptr++)) { if (c != ' ' && c != '\t' && c != '+') tmpStr[tpos++] = c; switch (state) { case ST_INIT: if (c == '-' || c == '+') state = ST_SIGN; else if (c == ' ' || c=='\t') state = ST_INIT; else if (c >= '0' && c <= '9') state = ST_NUM; else state = ST_ERR; break; case ST_SIGN: if (c >= '0' && c <= '9') state = ST_NUM; else state = ST_ERR; break; case ST_NUM: if (c >= '0' && c <= '9') state = ST_NUM; else state = ST_ERR; break; default: cout << "Internal state machine error\n"; state = ST_ERR; } } tmpStr[tpos] = '\0'; if (state != ST_NUM) return UTILS150_ERR_INVALID_FORMAT; int tmpval = atoi(str); char tmp[80]; itoa(tmpval, tmp, 10); if (tmpStr[0] == '-' && strcmp(tmp,tmpStr) != 0) return UTILS150_ERR_BELOW_MIN; if (tmpStr[0] != '-' && strcmp(tmp,tmpStr) != 0) return UTILS150_ERR_ABOVE_MAX; if (state == ST_NUM) return UTILS150_OK; else return UTILS150_ERR_INVALID_FORMAT; } /* int getValidInt(char* prompt); Arguments: char* prompt: prompt to display to user Returns: a valid integer entered by the user This function prompts the user and gets an integer from the keyboard. If the value entered is not a valid integer, this routine will discard it and reprompt the user. */ int getValidInt(char* prompt) { char linein[100]; bool valid = false; while (!valid) { cout << prompt; cin.getline(linein, 100, '\n'); int retval = isValidInt(linein); switch (retval) { case UTILS150_OK: valid = true; break; case UTILS150_ERR_INVALID_FORMAT: cout << "Error: Invalid integer number format.\n"; break; case UTILS150_ERR_BELOW_MIN: cout << "Error: Value below minimum integer range.\n"; break; case UTILS150_ERR_ABOVE_MAX: cout << "Error: Value above maximum integer range.\n"; break; } } return atoi(linein); } /* bool isValidFloat(char* ptr); Arguments: char* ptr: the address of the C-style string to be checked Returns: true if ptr points to a string which can be interpreted as a float, otherwise returns false 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 allowed. */ bool 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; } /* double getValidDouble(char* prompt); Arguments: char* prompt: prompt to display to user Returns: a valid double entered by the user This function prompts the user and gets a double from the keyboard. If the value entered is not a valid double, this routine will discard it and reprompt the user. Scientific notation is not supported, but there may be a single leading sign character (+ or -) and at most one decimal point. */ double getValidDouble(char* prompt) { char buf[40]; double num; bool valid = false; do { cout << prompt; cin.getline(buf, 40, '\n'); if (!isValidFloat(buf)) cout << "Error: Invalid floating point format\n"; else { num = atof(buf); valid = true; } } while (!valid); return num; } /* int getInt(char* msg, int min, int max); Arguments: msg: a pointer to a C-style string to be used as a prompt min: the minimum value allowed to be entered max: the maximum value allowed to be entered Returns: an integer entered by the user Prompts user with message passed in as argument and returns the integer value entered. The number must be between min and max (inclusive) or the user will be reprompted to enter another number. */ int getInt(char* msg, int min = INT_MIN, int max = INT_MAX) { char prompt[strlen(msg) + 40]; sprintf(prompt, "%s (%d - %d): ", msg, min, max); int num; do { num = getValidInt(prompt); if (num < min) cout << "Error: value below minimum allowed\n"; else if (num > max) cout << "Error: value above maximum allowed\n"; } while (nummax); return num; } /* double getDouble(char* msg, double min, double max); Arguments: msg: a pointer to a C-style string to be used as a prompt min: the minimum value allowed to be entered max: the maximum value allowed to be entered Returns: a double entered by the user Prompts user with message passed in as argument and returns the double value entered. The number must be between min and max (inclusive) or the user will be reprompted to enter another number. */ double getDouble(char* msg, double min, double max) { char prompt[strlen(msg) + 40]; sprintf(prompt, "%s (%f - %f): ", msg, min, max); double num; do { num = getValidDouble(prompt); if (num < min) cout << "Error: value below minimum allowed\n"; else if (num > max) cout << "Error: value above maximum allowed\n"; } while (nummax); return num; } /* char getChoice(char* prompt, char* allowed, bool upper = false); Arguments: char* prompt: the prompt to be shown to the user char* allowed: a string of allowed user response characters bool upper: flag that can make all user entries uppercase Returns: a char representing the user's valid choice; it will be converted to uppercase if needed if upper is true 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 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; } /* char processMenu(char *menu, char* prompt, char* allowed, bool upper=false); Arguments: char* menu: a menu to be shown to the user char* prompt: the prompt to be shown to the user char* allowed: a string of allowed user response characters bool upper: flag that can make all user entries uppercase Returns: a char representing the user's valid choice; it will be converted to uppercase if needed if upper is true Requires: getChoice(char*, char*, bool); 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 processMenu(char *menu, char* prompt, char* allowed, bool upper=false) { cout << menu; return getChoice(prompt, allowed, upper); } } #endif