// gradeLookUpDebug.cpp
//
// Illustrates use of output statements
// to debug a multi-function program.
//
// This program allows user to look up,
// modify, add, or delete student records
// records stored in text file quizRoster.txt.
// The records contain each student's ID,
// name, and 5 quiz scores.
#include "textUtility2.h" // includes <iostream>
#include <fstream>
const int NUMBER_OF_QUIZZES = 5;
typedef float QuizArray[NUMBER_OF_QUIZZES];
const int MAX_NAME_LENGTH = 40; // last name first;
// includes spaces between
// parts of name.
typedef char NameText[MAX_NAME_LENGTH + 1];
struct Student {
int iD;
NameText name;
QuizArray quizzes;
};
const int MAX_NUMBER_OF_STUDENTS = 40;
typedef Student Roster[MAX_NUMBER_OF_STUDENTS];
// ----------------------------------------
// ***** High-level tasks of program: *****
// ----------------------------------------
int lookUpStudent(const Roster roster,
int numberOfStudents);
void changeStudent(Roster roster,
int numberOfStudents);
void addNewStudent(Roster roster,
int& numberOfStudents);
void deleteStudent(Roster roster,
int& numberOfStudents);
// ------------------------------------------
// ***** Auxiliary to high-level tasks: *****
// ------------------------------------------
int askIDAndSearch(const Roster roster,
int numberOfStudents,
int& index);
// ----------------------------
// ***** Input functions: *****
// ----------------------------
bool readStudents(const char inputFilename[],
Roster roster,
int& numberOfStudents);
bool askForID(int& iD);
bool askForStudentData(Student& student);
bool userSaysYes(const char question[]);
char askSelection(const char question[],
const char choices[]);
void skipRestOfLine(istream& in);
void skipRestOfLine(istream& in, char& x);
// ------------------------------------
// ***** Computational functions: *****
// ------------------------------------
int searchForID(int iD,
const Roster roster,
int numberOfStudents);
char toUpperCase(char x);
bool equalsIgnoreCase(char x, char y);
// -----------------------------
// ***** Output functions: *****
// -----------------------------
void displayChoices();
void printStudent(const Student& student, ostream& out);
bool writeStudents(const char outputFilename[],
const Roster roster,
int numberOfStudents);
// ---------------------------------------------
// ******** End of declaration section *********
// ---------------------------------------------
/*
* int main()
*
* This program allows user to look up,
* modify, add, or delete student records
* records stored in text file quizRoster.txt.
* The records contain each student's ID,
* name, and 5 quiz scores.
*
* The program first loads records from
* text file, then displays menu of options.
* After the user's requested task is complete,
* the user is asked whether to display the
* menu again. The menu is thus displayed
* repeatedly, each time allowing the user to
* select a subsequent task, until the user asks
* to quit or asks not to see the menu again.
*
* Global variables:
* cin - input stream from keyboard.
* An object of class istream,
* declared in header file <iostream>.
* cout - output stream to terminal window.
* An object of class ostream,
* declared in header file <iostream>.
*/
int main()
{
// Read student data from data file:
const char filename[] = "quizRoster.txt";
Roster students;
int numberOfStudents;
if ( ! readStudents(filename, // uses cout
students,
numberOfStudents) )
return 1;
// Later, whenever this program quits for
// whatever reason, all modifications to
// students, the Roster, should be saved
// to the same data file from which it
// was originally loaded.
// Allow user to select tasks, in a loop
// which quits when the user chooses or
// when input fails:
do { // while user wants to continue
// Display menu and prompt user selection:
displayChoices();
char question[] = "What do you want to do?";
char choice = askSelection(question, // uses cin
"LCADQ"); // and cout
// Perform the user-selected task:
switch ( choice )
{
case 'L':
lookUpStudent(students, // uses cin
numberOfStudents); // and cout
break;
case 'C':
changeStudent(students, // uses cin
numberOfStudents); // and cout
break;
case 'A':
addNewStudent(students, // uses cin
numberOfStudents); // and cout
break;
case 'D':
deleteStudent(students, // uses cin
numberOfStudents); // and cout
break;
case 'Q': // quit
// Save modified data before quitting:
writeStudents(filename, // uses cout
students,
numberOfStudents);
return 0;
// No default case is needed, because the call
// to askSelection ensures a valid response.
default: // ***** NEEDED FOR DEBUGGING ONLY *****
cout << "switch in main: "
<< "default case reached." << endl;
} // switch
// Quit if input has failed:
if ( !cin ) {
// Save modified data before quitting:
writeStudents(filename, // uses cout
students,
numberOfStudents);
return 1;
} // if
} while ( userSaysYes("Display menu again?") );
// Save modified data before quitting:
writeStudents(filename, // uses cout
students,
numberOfStudents);
return 0;
} // function main
// ----------------------------------------
// ***** High-level tasks of program: *****
// ----------------------------------------
/*
* int lookUpStudent(const Roster roster,
* int numberOfStudents)
*
* Allows user to look up information about
* a student in roster. The user is prompted
* for an ID number. If a student with that
* ID is found, the contents of the record
* are displayed to the terminal. Otherwise,
* "Not found." is displayed.
*
* Parameters:
* roster - array of Student records.
* numberOfStudents - count of records
* in roster.
*
* Global variables:
* cin - input stream from keyboard.
* An object of class istream,
* declared in header file <iostream>.
* Precondition: The state of cin is true,
* and cin is ready to begin a line.
* Postcondition: The state of in is still
* true, and cin has ended a line.
* cout - output stream to terminal window.
* An object of class ostream,
* declared in header file <iostream>.
* Precondition: The state of cout is true,
* and cout is ready to begin a line.
* Postcondition: The state of cout is still
* true, and cout has NOT ended a line,
* so that input is typed next to prompt.
* (Echoed input then ends displayed line.)
*
* Returns:
* index of location of student with
* requested ID, if found.
* -1 if not found.
*/
int lookUpStudent(const Roster roster,
int numberOfStudents)
{
// Ask user for student ID and search for it
// in the roster, quitting if input fails:
int index;
askIDAndSearch(roster, // uses cin and cout
numberOfStudents,
index);
if ( !cin ) // (Error message has already been printed.)
return -1; // to indicate not found
// Print information about student, if found;
// otherwise indicate not found:
if ( index == -1 ) // not found
cout << "Not found." << endl;
else
printStudent(roster[index], cout);
return index;
} // function lookUpStudent
/*
* void changeStudent(Roster roster,
* int numberOfStudents)
*
* Modifies a student reoord in roster.
* Prompts user for ID, then searches
* for record with that ID. If that
* record is found, user is prompted
* for new data for that record.
* Otherwise, "Not found." is displayed.
*
* Parameters:
* roster - array of Student records.
* Precondition: roster contains
* meaningful records in locations
* 0 to (numberOfStudents-1).
* Postcondition: one record has
* been modified, if requested ID
* was found. Unchanged otherwise.
* numberOfStudents - count of records
* in roster.
*
* Global variables:
* cin - input stream from keyboard.
* An object of class istream,
* declared in header file <iostream>.
* Precondition: The state of cin is true,
* and cin is ready to begin a line.
* Postcondition: The state of in is still
* true, and cin has ended a line.
* cout - output stream to terminal window.
* An object of class ostream,
* declared in header file <iostream>.
* Precondition: The state of cout is true,
* and cout is ready to begin a line.
* Postcondition: The state of cout is still
* true, and cout has NOT ended a line,
* so that input is typed next to prompt.
* (Echoed input then ends displayed line.)
*/
void changeStudent(Roster roster,
int numberOfStudents)
{
// Ask for ID of record to be changed,
// then find its index in the roster
// and display contents of record:
int index = lookUpStudent( // uses cin and cout
roster,
numberOfStudents);
// Quit if student has not been found,
// or if input has failed:
if ( (index == -1) || !cin )
return; // (Error message has already been printed.)
// We are now ready to ask the user for data
// for a new student record and then copy it
// into the roster, replacing old record.
// A temporary second record is needed,
// rather than immediate, direct modification
// of the roster itself, to avoid an
// incompletely modified entry in the roster
// in the event of input failure while
// interacting with the user.
// Create new student record:
Student student;
student.iD = roster[index].iD;
if ( ! askForStudentData(student) ) // uses cin
return; // and cout
// Copy into roster:
roster[index] = student;
} // function changeStudent
/*
* void addNewStudent(Roster roster,
* int& numberOfStudents)
*
* Adds a Student record to the roster, if
* the requested new record is not a duplicate
* of one already in the roster. The user
* is prompted for an ID number. If there
* is not already a record with that ID
* number in the roster, the user is prompted
* for other student data, and the new record
* is added.
*
* Parameters:
* roster - array of Student records.
* Precondition: roster contains
* meaningful records in locations
* 0 to (numberOfStudents-1).
* Postcondition: one record has
* been added, if there is not
* already a record with requested
* ID in roster. Unchanged otherwise.
* numberOfStudents - count of records
* in roster.
* Precondition: numberOfStudents is
* count of meaningful records
* previously in roster.
* Postcondition: numberOfStudents
* is increased by 1 if a record
* is added. Unchanged otherwise.
*
* Global variables:
* cin - input stream from keyboard.
* An object of class istream,
* declared in header file <iostream>.
* Precondition: The state of cin is true,
* and cin is ready to begin a line.
* Postcondition: The state of in is still
* true, and cin has ended a line.
* cout - output stream to terminal window.
* An object of class ostream,
* declared in header file <iostream>.
* Precondition: The state of cout is true,
* and cout is ready to begin a line.
* Postcondition: The state of cout is still
* true, and cout has NOT ended a line,
* so that input is typed next to prompt.
* (Echoed input then ends displayed line.)
*/
void addNewStudent(Roster roster,
int& numberOfStudents)
{
// A student can be added only if the roster
// is not already full:
if ( numberOfStudents >= MAX_NUMBER_OF_STUDENTS ) {
cout << "Roster full." << endl;
return;
} // if
// Ask user for student ID and search for it
// in the roster, quitting if input fails:
int index;
int iD = askIDAndSearch(roster, // uses cin and cout
numberOfStudents,
index);
if ( ! cin )
return; // (Error message has already been printed.)
// It's OK to add a new student with a particular
// ID only if a student with that ID has not
// been found. If such a student has been
// found, print an error message and quit.
if ( index != -1 ) {
cout << "Student already in roster:" << endl;
printStudent(roster[index], cout);
return;
} // if
// Add a student at the next available
// location, quitting if input fails:
index = numberOfStudents;
roster[index].iD = iD;
if ( ! askForStudentData(roster[index]) ) // uses cin
return; // and cout
// Now that a student has been
// added successfully, increment
// count of students in roster:
numberOfStudents++;
} // function addNewStudent
/*
* Deletes a Student record from roster,
* if a record with a user-specified ID is
* found in the roster. The user is
* prompted for an ID number. If it is
* found, the contents of the record are
* displayed, and the user is asked to
* confirm desire to delete. If the user
* says yes, the record is deleted. If
* the user says no, or if a record with
* the requested ID was not found, roster
* is unchanged.
*
* Parameters:
* roster - array of Student records.
* Precondition: roster contains
* meaningful records in locations
* 0 to (numberOfStudents-1).
* Postcondition: one record has
* been deleted, if requested ID
* was found, and all records with
* a higher index are moved one
* location down to fill gap. If
* requested ID was not found,
* roster is unchanged.
* numberOfStudents - count of records
* in roster.
* Precondition: numberOfStudents is
* count of meaningful records
* previously in roster.
* Postcondition: numberOfStudents
* is decreased by 1 if a record
* is deleted. Unchanged otherwise.
*
* Global variables:
* cin - input stream from keyboard.
* An object of class istream,
* declared in header file <iostream>.
* Precondition: The state of cin is true,
* and cin is ready to begin a line.
* Postcondition: The state of in is still
* true, and cin has ended a line.
* cout - output stream to terminal window.
* An object of class ostream,
* declared in header file <iostream>.
* Precondition: The state of cout is true,
* and cout is ready to begin a line.
* Postcondition: The state of cout is still
* true, and cout has NOT ended a line,
* so that input is typed next to prompt.
* (Echoed input then ends displayed line.)
*/
void deleteStudent(Roster roster,
int& numberOfStudents)
{
// Ask for ID of record to be changed,
// then find its index in the roster
// and display contents of record:
int index = lookUpStudent( // uses cin and cout
roster,
numberOfStudents);
// Quit if student has not been found,
// or if input has failed:
if ( (index == -1) || !cin )
return; // (Error message has already been printed.)
// Assertion: If we have reached this point,
// a student record has been found and displayed.
// Verify user's intent to delete this student:
char question[] = "Do you really want to delete this student?";
bool deleteOK = userSaysYes(question);
// If user has said yes, delete student:
if ( deleteOK ) {
// Slide all higher-indexed student records down
// one location each, overwriting record to delete:
for ( int i = index + 1; i < numberOfStudents; i++ )
roster[i-1] = roster[i];
// Decrement count of students in roster:
numberOfStudents--;
} // if
} // function deleteStudent
// ------------------------------------------
// ***** Auxiliary to high-level tasks: *****
// ------------------------------------------
/*
* int askIDAndSearch(const Roster roster,
* int numberOfStudents,
* int& index)
*
* Prompts user for a student ID, then
* searches for it in roster. Returns
* requested ID and sets parameter
* index to indicate location in roster.
*
* Parameters:
* roster - array of Student records.
* numberOfStudents - count of records
* in roster.
* index - location of requested student's
* record in roster.
* Preconditions: none.
* Postconditions: index is location
* of record of student with requested
* ID, if found; -1 if not found.
*
* Global variables:
* cin - input stream from keyboard.
* An object of class istream,
* declared in header file <iostream>.
* Precondition: The state of cin is true,
* and cin is ready to begin a line.
* Postcondition: The state of in is still
* true, and cin has ended a line.
* cout - output stream to terminal window.
* An object of class ostream,
* declared in header file <iostream>.
* Precondition: The state of cout is true,
* and cout is ready to begin a line.
* Postcondition: The state of cout is still
* true, and cout has NOT ended a line,
* so that input is typed next to prompt.
* (Echoed input then ends displayed line.)
*
* Returns:
* The ID number entered by the user,
* if user entered it successfully.
* -1 if input fails.
*/
int askIDAndSearch(const Roster roster,
int numberOfStudents,
int& index)
{
// Ask user for student ID,
// quitting if input fails:
int iDToFind;
if ( ! askForID( iDToFind ) ) { // uses cin
// and cout
index = -1; // to indicate not found
return -1; // to indicate input failure
} // if
// Search for student with requested ID,
// obtaining index of student in roster
// (or -1 if not found), then return ID:
index = searchForID(iDToFind,
roster,
numberOfStudents);
return iDToFind;
} // function askIDAndSearch
// ----------------------------
// ***** Input functions: *****
// ----------------------------
/*
* bool readStudents(const char inputFilename[],
* Roster roster,
* int& numberOfStudents)
*
* Reads all the student information in
* the specified text file and loads it,
* as one record per student, into roster.
*
* An error message is printed if the
* file is not found, is not readable,
* or contains a number format error
* beyond the first item on a line.
*
* Parameters:
* inputFilename - filename of text file
* to read from. The format of the
* text file must be: one student
* per line, with columns for (1) ID,
* (2) 5 quiz scores, and (3) names,
* last names first. All data items
* on a line are separated by
* whitespace (spaces or tabs).
* roster - an array of Student records.
* Precondition: none.
* Postcondition: roster contains all
* the student information in the
* input file, if input has not failed.
* numberOfStudents - count of records in roster.
* Precondition: none.
* Postcondition: number of students
* is the number of records that
* have been read into roster.
*
* Global variable:
* cout - output stream to terminal window.
* An object of class ostream,
* declared in header file <iostream>.
* Precondition: The state of cout is true,
* and cout is ready to begin a line.
* Postcondition: The state of cout is still
* true, and cout has ended a line.
*
* Returns:
* true if input was successful, false otherwise.
*/
bool readStudents(const char inputFilename[],
Roster roster,
int& numberOfStudents)
{
// Prepare to read from input file:
ifstream inFile;
inFile.open(inputFilename);
if ( !inFile ) {
cout << "Could not read "
<< inputFilename << "." << endl;
return false;
} // if
// Read data for each student into a location
// in roster array corresponding to the number
// of student records previously read:
numberOfStudents = 0; // records previously read
while ( numberOfStudents < MAX_NUMBER_OF_STUDENTS ) {
// Attempt to read ID, first item on line:
inFile >> roster[numberOfStudents].iD;
// Quit reading if at end of file:
if ( !inFile )
break;
// Read quiz scores:
for ( int i = 0; i < NUMBER_OF_QUIZZES; i++ )
inFile >> roster[numberOfStudents].quizzes[i];
// Check for format error:
if ( !inFile ) {
cout << inputFilename
<< " format error at line "
<< (numberOfStudents + 1) << endl;
return 1;
} // if
// skip white space:
char x;
do {
inFile.get(x);
} while ( x == ' ' || x == '\t' );
// Read name:
int nameIndex = 0;
while ( x != '\n' && nameIndex < MAX_NAME_LENGTH )
{
roster[numberOfStudents].name[nameIndex] = x;
inFile.get(x);
nameIndex++;
} // while
// Skip any remaining characters on the line:
skipRestOfLine(inFile, x);
while ( x != '\n')
inFile.get(x);
// Terminate C-string:
roster[numberOfStudents].name[nameIndex] = '\0';
// next Student in array:
numberOfStudents++;
} // while
// Release handle on file, so it can be re-used:
inFile.close();
return true;
} // function readStudents
/*
* bool askForID(int& iD)
*
* Prompts the user to enter an ID number,
* and then reads it.
*
* An error message is printed if input fails.
*
* Parameter:
* iD - a student's ID number.
* Precondition: none.
* Postcondition: iD is number typed by user.
*
* Global variables:
* cin - input stream from keyboard.
* An object of class istream,
* declared in header file <iostream>.
* Precondition: The state of cin is true.
* Postcondition: The state of in is still
* true, and cin has ended a line.
* cout - output stream to terminal window.
* An object of class ostream,
* declared in header file <iostream>.
* Precondition: The state of cout is true,
* and cout is ready to begin a line.
* Postcondition: The state of cout is still
* true, and cout has NOT ended a line,
* so that input is typed next to prompt.
* (Echoed input then ends displayed line.)
*
* Returns:
* true if input was successful,
* false otherwise.
*/
bool askForID(int& iD)
{
cout << "Enter student ID number:>";
cin >> iD;
if ( !cin ) {
cout << "You must enter integers only." << endl;
return false;
} // if
skipRestOfLine(cin);
return true;
} // function askForID
/*
* bool askForStudentData(Student& student)
*
* Prompts user for all data about a student
* except the ID number.
*
* An error message is printed if input fails.
*
* Parameter:
* student - a Student record.
* Preconditions: none.
* Postconditions: student contains
* data entered by user for all fields
* except iD, which is unchanged.
*
* Global variables:
* cin - input stream from keyboard.
* An object of class istream,
* declared in header file <iostream>.
* Precondition: The state of cin is true,
* and cin is ready to begin a line.
* Postcondition: The state of in is still
* true, and cin has ended a line.
* cout - output stream to terminal window.
* An object of class ostream,
* declared in header file <iostream>.
* Precondition: The state of cout is true,
* and cout is ready to begin a line.
* Postcondition: The state of cout is still
* true, and cout has NOT ended a line,
* so that input is typed next to prompt.
* (Echoed input then ends displayed line.)
*
* Returns:
* true if input is successful,
* false otherwise.
*/
bool askForStudentData(Student& student)
{
// Prompt for student name:
cout << "Enter student name (last name first): ";
// Read name:
char x;
cin.get(x);
int nameIndex = 0;
while ( x != '\n' && nameIndex < MAX_NAME_LENGTH )
{
student.name[nameIndex] = toUpperCase(x);
cin.get(x);
nameIndex++;
} // while
// Skip any remaining characters on the line:
skipRestOfLine(cin, x);
// Terminate C-string:
student.name[nameIndex] = '\0';
// Prompt for quiz scores:
cout << "Enter " << NUMBER_OF_QUIZZES
<< " quiz scores:";
// Read quiz scores:
for ( int i = 0; i < NUMBER_OF_QUIZZES; i++ ) {
cin >> student.quizzes[i];
if ( !cin ) {
cout << "Non-numeric quiz score entry." << endl;
return false;
} // if
} // for i
// Finish input line and quit:
skipRestOfLine(cin);
return true;
} // function askForStudentData
/*
* bool userSaysYes(const char question[])
*
* Prompts user to answer a yes/no question,
* then reads the user's answer of 'Y', 'y',
* 'N', or 'n'. If the user's answer is
* anything other than 'Y', 'y','N', or 'n',
* then the user is prompted repeatedly until
* a valid response is obtained.
*
* Parameter:
* question - a yes/no question to ask user.
*
* Global variables:
* cin - input stream from keyboard.
* An object of class istream,
* declared in header file <iostream>.
* Precondition: The state of cin is true,
* and cin is ready to begin a line.
* Postcondition: The state of in is still
* true, and cin has ended a line.
* cout - output stream to terminal window.
* An object of class ostream,
* declared in header file <iostream>.
* Precondition: The state of cout is true,
* and cout is ready to begin a line.
* Postcondition: The state of cout is still
* true, and cout has NOT ended a line,
* so that input is typed next to prompt.
* (Echoed input then ends displayed line.)
*
* Returns:
* true if user typed 'Y' or 'y'.
* false if user typed 'N' or 'n'.
*/
bool userSaysYes(const char question[])
{
char choices[] = "YN";
char x = askSelection(question, choices);
cout << "userSaysYes: x=" << x << " returns: "; //****** DEBUG
cout << "userSaysYes: returns "
<< equalsIgnoreCase(x, 'Y') << endl; //****** DEBUG
return equalsIgnoreCase(x, 'Y');
} // function userSaysYes
/*
* char askSelection(const char question[],
* const char choices[])
*
* Prompts user to answer a question
* by typing one of the characters in
* a list of permitted responses.
* If the user types an invalid response,
* the user is prompted repeatedly until
* a valid response is obtained.
*
* Parameters:
* question - question to ask user, not
* including list of valid responses.
* choices - array of valid character
* responses. No two of the characters
* should be equal, ignoring case.
* (The array is terminated by '\0',
* like any C-string.)
*
* Global variables:
* cin - input stream from keyboard.
* An object of class istream,
* declared in header file <iostream>.
* Precondition: The state of cin is true,
* and cin is ready to begin a line.
* Postcondition: The state of in is still
* true, and cin has ended a line.
* cout - output stream to terminal window.
* An object of class ostream,
* declared in header file <iostream>.
* Precondition: The state of cout is true,
* and cout is ready to begin a line.
* Postcondition: The state of cout is still
* true, and cout has NOT ended a line,
* so that input is typed next to prompt.
* (Echoed input then ends displayed line.)
*
* Returns:
* the user's response, guaranteed to be one
* of the characters in choices, uppercased.
*/
char askSelection(const char question[],
const char choices[])
{
// Determine length of choices string:
int index = 0;
while ( choices[index] != '\0' )
index++;
const int length = index;
// It is pointless to call this function for
// fewer than 2 choices. In that case,
// print an error message and quit:
if (length < 2) {
cout << "Error: askSelection called with only "
<< length << " choice." << endl;
return choices[0];
} // if
// Assertion: If we have reached this point,
// the length of choices is >= 2.
// Prompt user with question and choices:
cout << question << " ("
<< toUpperCase(choices[0]);
for ( int i = 1; i < length; i++ )
cout << "/" << toUpperCase(choices[i]);
cout << "):>";
// Read user response. If it's not a valid
// selection, prompt user repeatedly until
// a valid response is obtained.
char x;
while ( true ) {
// Read character typed by user,
cin.get(x);
// Finish reading line, but only if
// x is not already '\n'.
char y = x; // So that, if x is '\n'
skipRestOfLine(cin, y);
// Assertions: cin has just read '\n';
// and y is now '\n', but x is still
// the first character on the line.
cout << "askSelection: x=" << x << endl; // ***** DEBUG
// Determine whether character x
// is a valid selection:
bool match = false;
for ( int i = 0; (i < length) && !match; i++ ) {
if ( equalsIgnoreCase(x, choices[i]) )
match = true;
cout << "askSelection: choices[" << i << "]="
<< choices[i] << endl; // ***** DEBUG
} // for i
cout << "askSelection: match=" << match << endl; // ***** DEBUG
// If x is a valid selection, there
// is no need to nag user, so quit,
// returning user's selection:
if ( match )
return toUpperCase(x);
// Nag user for valid selection:
if ( length == 2 )
cout << "Please enter "
<< toUpperCase(choices[0]) << " or "
<< toUpperCase(choices[1]) << ":>";
else {
cout << "Please enter ";
for ( int i = 0; i < length - 1; i++ )
cout << toUpperCase(choices[i]) << ", ";
cout << " or "
<< toUpperCase(choices[length-1])
<< ":>";
} // else
} // while
} // function askSelection
/*
* void skipRestOfLine(istream& in)
*
* Skips one character, then skips any
* remaining characters on the line,
* up to and including end-of-line marker.
* If a call to this function is intended
* only to finish the current line of input
* and not skip an entire line, then cin
* must NOT be ready to begin a new line.
*
* The end-of-line marker is assumed to
* consist of or end with a newline
* character ('\n').
*
* Parameter:
* in - an input stream.
* Precondition: The state of in is true.
* Postcondition: The state of in is still
* true, and in has ended a line.
*/
void skipRestOfLine(istream& in)
{
char x;
do {
in.get(x);
} while ( x != '\n' ); // Unix end-of-line
} // function skipRestOfLine(istream&)
/*
* void skipRestOfLine(istream& in, char& x)
*
* Reads any remaining characters on the
* current line of input.
*
* The end-of-line marker is assumed to
* consist of or end with a newline
* character ('\n').
*
* in - an input stream.
* Precondition: The state of in is true.
* Postcondition: The state of in is still
* true, and in has ended a line. If
* x was originally '\n' (which should
* be the case only if in was originally
* ready to begin a reading a line), then
* no more characters have been read.
* x - the character most recently read,
* either by this function (if it reads
* any characters) or previously.
* Precondition: x is the character most
* recently read before this function
* was called.
* Postcondition: x equals '\n'.
*/
void skipRestOfLine(istream& in, char& x)
{
while ( x != '\n' ) // Unix end-of-line
in.get(x);
} // function skipRestOfLine(istream&, char&)
// ------------------------------------
// ***** Computational functions: *****
// ------------------------------------
/*
* int searchForID(int iD,
* const Roster roster,
* int numberOfStudents)
*
* Searches roster for a student with
* a particular ID number.
*
* Parameters:
* iD - ID number to search for.
* roster - array of Student records.
* numberOfStudents - count of records
* in roster.
*
* Returns:
* index of location of student with
* requested ID, if found.
* -1 if not found.
*/
int searchForID(int iD,
const Roster roster,
int numberOfStudents)
{
for (int i = 0; i < numberOfStudents; i++)
if ( iD == roster[i].iD )
return i;
return -1;
} // function searchForID
/*
* Returns an uppercase version of
* a lowercase character, or the
* character itself, if the original
* character was not lowercase.
*
* Parameter:
* x - any character.
*
* Returns:
* An uppercase character corresponding
* to x, if x was lowercase.
* x, otherwise.
*/
char toUpperCase(char x)
{
if ( x >= 'a' && x <= 'z' ) // if lowercase,
return (x + 'A' - 'a'); // return uppercase version.
return x; // else, return unchanged.
} // function toUpperCase
/*
* bool equalsIgnoreCase(char x, char y)
*
* Determines whether two characters
* are equal, ignoreing case.
*
* Parameters:
* x - one of the characters to be compared.
* y - the other character to be compared.
*
* Returns:
* true if x and y are equal, ignoring case.
* false otherwise.
*/
bool equalsIgnoreCase(char x, char y)
{
cout << "equalsIgnoreCase: x=" << x
<< " y=" << y << endl; // **** DEBUG
return ( toUpperCase(x) == toUpperCase(y) );
} // function equalsIgnoreCase
// -----------------------------
// ***** Output functions: *****
// -----------------------------
/*
* void displayChoices()
*
* Displays, to the terminal windo, a menu of
* this program's high-level capabilities.
*
* Global variable:
* cout - output stream to terminal window.
* An object of class ostream,
* declared in header file <iostream>.
* Precondition: The state of cout is true,
* and cout is ready to begin a line.
* Postcondition: The state of cout is still
* true, and cout has ended a line.
*/
void displayChoices()
{
cout << "Welcome to Quiz Grade Look-Up." << endl;
cout << "You may do any of the following:"
<< endl;
cout << endl;
cout << " L) Look up a student's quiz grades."
<< endl;
cout << " C) Change a student's record."
<< endl;
cout << " A) Add a new student." << endl;
cout << " D) Drop (delete) a student."
<< endl;
cout << " Q) Quit." << endl;
cout << endl;
} // function displayChoices
/*
* void printStudent(const Student& student, ostream& out)
*
* Prints a line of text containing information
* about one student, in a format conducive to neat
* columns if this function is called repeatedly.
* Prints a student's ID number, quiz scores, and name.
*
* Parameters:
* student - record containing information about
* one student.
* out - an output stream.
* Precondition: The state of out is true,
* and out is ready to begin a line.
* Postcondition: The state of out is still
* true, and out has ended a line.
*/
void printStudent(const Student& student, ostream& out)
{
const int ID_WIDTH = 9;
int leadingZeroes = ID_WIDTH - digitCount(student.iD);
for ( int i = 0; i < leadingZeroes; i++ )
out << "0";
out << student.iD;
out << " ";
for ( int i = 0; i < NUMBER_OF_QUIZZES; i++ )
printRightJustified(student.quizzes[i], 1, 5, out);
out << " " << student.name << endl;
} // function printStudent
/*
* bool writeStudents(const char outputFilename[],
* const Roster roster,
* int numberOfStudents)
*
* Outputs all the student records in roster
* to the specified text file.
*
* An error message is printed if the
* file cannot be written to.
*
* Parameters:
* outputFilename - filename of text file
* to write to.
* roster - an array of Student records.
* numberOfStudents - count of records in roster.
*
* Global variable:
* cout - output stream to terminal window.
* An object of class ostream,
* declared in header file <iostream>.
* Precondition: The state of cout is true,
* and cout is ready to begin a line.
* Postcondition: The state of cout is still
* true, and cout has ended a line.
*
* Returns:
* true if output was successful, false otherwise.
*/
bool writeStudents(const char outputFilename[],
const Roster roster,
int numberOfStudents)
{
// Prepare to write to file:
ofstream outFile;
outFile.open(outputFilename);
if ( !outFile ) {
cout << "Could not write to "
<< outputFilename << "." << endl;
return false;
} // if
// Write all students in roster to output file:
for ( int i = 0; i < numberOfStudents; i++ )
printStudent(roster[i], outFile);
// Release handle on file, so it can be re-used:
outFile.close();
return true;
} // function writeStudents