/*******************************************************************************
Files: testbank.txt - Sequential Access - Input &
Output - - Storage of all questions
*******************************************************************************/
#include <stdlib.h>
#include <iostream.h>
#include <iomanip.h>
#include <string.h>
#include <conio.h>
#include <fstream.h>
#include <strstrea.h>
#include <ctype.h>
#include <time.h>
// Maximum number of questions that can be stored in
the array
const int iMAX_QUESTIONS = 10;
const int iMENU_ITEMS = 8;
// File names
const char cTESTBANK_FILE_NAME [] = "testbank.txt";
// First line of all output
const char cMAIN_HEADING[] = "TEST BANK MAINTENANCE
SYSTEM";
// Screen Subheadings and other messages
const char cADD_QUESTION_HEADING[] = "*** ADD QUESTION
***",
cQUESTION_ADDED_MESSAGE[] = "QUESTION ADDED",
cMODIFY_QUESTION_HEADING[] = "*** MODIFY QUESTION
***",
cQUESTION_MODIFIED_MESSAGE[] = "QUESTION MODIFIED",
cQUESTION_NOT_MODIFIED_MESSAGE[] = "QUESTION NOT
MODIFIED",
cDELETE_QUESTION_HEADING[] = "*** DELETE QUESTION
***",
cQUESTION_DELETED_MESSAGE[] = "QUESTION DELETED",
cQUESTION_NOT_DELETED_MESSAGE[] = "QUESTION NOT
DELETED",
cGENERATE_QUESTIONS_HEADING[] = "*** GENERATE QUESTION
SET ***",
cQUESTIONS_GENERATED_MESSAGE[] = "QUESTIONS
GENERATED",
cGENERATED_QUESTIONS_HEADING [] = "*** GENERATED
QUESTIONS ***",
cANY_KEY_MSG[] = "PRESS ANY KEY TO CONTINUE",
cGOODBYE_MESSAGE[] = "THANK YOU FOR USING THE TEST
BANK SYSTEM";
// Menu array
const char *cMENU[iMENU_ITEMS] = {" *** MAIN MENU
***",
"Please select an option:",
"a. Add question",
"b. Modify question",
"c. Delete question",
"d. Generate question set",
"x. Exit",
"Option? "};
// menu options
const char cEXIT_OPTION = 'X',
cNEW_QUESTION_OPTION = 'A',
cMODIFY_QUESTION_OPTION = 'B',
cDELETE_QUESTION_OPTION = 'C',
cGENERATE_QUESTIONS_OPTION = 'D';
// Used to test for mandatory and optional input
fields
const char cMANDATORY = 'M';
const char cOPTIONAL = 'O';
// Screen positioning - columns
const int iLEFT_MARGIN = 5;
const int iCOL_POS = 30;
// Screen positioning - rows
const int iHEADING_LINE = 2;
const int iERROR_LINE = 20;
const int iEND_LINE = 22;
const int iQUESTION_NUMBER_LINE = iHEADING_LINE + 4;
const int iQUESTION_LINE = iHEADING_LINE + 6;
const int iANSWER_LINE = iHEADING_LINE + 8;
const int iDIFFICULTY_LEVEL_LINE = iHEADING_LINE + 10;
const int iOPTION_LINE = iHEADING_LINE + 12;
const int iDELETEMODIFY_QUESTION_LINE = iHEADING_LINE
+ 14;
// Validation strings
const char cANSWERS[] = "TF";
const char cVALID_MENU_OPTIONS[]="ABCDX";
const char cVALID_RESPONSE_LIST[] = "YN";
// Input field maximum lengths
const int iQUESTION_LEN = 150;
// error messages
const char cERR_MENU[] = "ERROR - Menu option must be
";
const char cERR_ARRAY_MAX_MSG[]
= "ERROR - Maximum number of questions reached. Unable
to add more.";
const char cERR_FILE_OPEN_MSG[] = "ERROR - Unable to
open file: ";
const char cERR_NO_QUESTIONS_MSG[]
= "ERROR - No questions yet recorded. Unable to
fulfill your request";
const char cERR_MANDATORY_MSG[] = "ERROR - Mandatory
field. Please enter";
const char cERR_ANSWER[] = "ERROR - Answer must be ";
const char cERR_INVALID_YN_RESPONSE[] = "ERROR -
Response must be ";
const char cERR_INVALID_DIFFICULTY[] = "ERROR! -
Difficulty Level must be between ";
const char cERR_INVALID_QUESTION_NUMBER[] = "ERROR! -
Question Number must be between ";
const char cERR_ALREADY_DELETED[] = "ERROR! - This
question has been deleted.";
const char cERR_NO_QUESTIONS[] = "ERROR! - No
available questions to generate.";
const char cERR_INVALID_NUM_OF_QUESTIONS[] = "ERROR! -
Number of questions must be between ";
// enumerated difficulty levels
const enum {EASY = 1, MEDIUM, DIFFICULT};
// enumerated question status
const enum {INITIAL = -1, ACTIVE, DELETED};
// date format
struct stDate
{
int iDay;
int iMonth;
int iYear;
};
// question record format
struct stQuestion
{
int iQuestionNumber;
char cQuestion[iQUESTION_LEN];
char cAnswer;
int iDifficultyLevel;
int iStatus;
};
stQuestion stAddNewquestion(int iNewQuestionNumber);
void vInitialiseQuestions(stQuestion
stQuestionArray[], int iArraySize);
stQuestion stReadQuestionRecord (ifstream
&questionFile);
int iReadQuestionFile(stQuestion stQuestionArray[]);
void vInputString ( int iLineNum, char cInput[],
const int iInputLength, char cMand);
void vDisplayTemplate(const char cHeading[]);
char cMenu(bool bIfInitial = false, int iNumQuestions
= 0);
char cInputLetter (int iLineNum, const char
cValidList[],
const char cErrorMsg[], char cMand);
stDate dtInputBirthDate ();
bool bValidateDate(char cTemp[], stDate &dtInputDate);
char cValidateLetter(int iPromptLine, char
cValidList[]);
int iValidateIntRange(int iMin, int iMax, const char
cErrorMsg[],
int iLineNum, char cMand);
bool bValidateString (char cTemp[], const char
cValidList[],
const char cErrorMsg[]);
void vDisplayRecord(stQuestion stQuestionRecord);
void vModifyQuestion(stQuestion stQuestionArray[]);
void vDeleteQuestion(stQuestion stQuestionArray[]);
void vGenerateQuestions(stQuestion stQuestionArray[],
int iNumQuestions);
int *vRandomlyGenerateNumbers(int
iNumbersToGenerate,int iMaximumQuestions,
stQuestion stQuestionArray[]);
void vDisplayGeneratedQuestions(int
*iGeneratedQuestions,
int iNumGeneratedQuestions,
stQuestion stQuestionArray[]);
void vHeadings();
int vWriteFile (stQuestion stQuestionArray[], int
iNumQuestions);
void vOutputArray(stQuestion stQuestionArray[], int
iArraySize);
void vPause();
int iDetermineLastActiveQuestionNum(stQuestion
stQuestionArray[], int iArraySize);
void main()
{
// array used to store all conatct information
stQuestion stQuestionArray[iMAX_QUESTIONS];
// initialise questions to null
vInitialiseQuestions(stQuestionArray, iMAX_QUESTIONS);
// count of the number of questions in the array. The
first
// value is the number of records read from the file.
int iNumQuestions = 0, iActualNumQuestions = 0;
// the first available i.e. undeleted question index.
int iQuestionIndex;
// user selected menu option
char cOption;
// count of number of questions written to the file at
the end.
int iQuestionsWritten;
// read question file and store all records in the
array
iNumQuestions = iReadQuestionFile(stQuestionArray);
// display the menu
cOption = cMenu(true, iNumQuestions);
// evaluate the entered option
while (cOption != cEXIT_OPTION)
{
if (cOption == cNEW_QUESTION_OPTION)
{
// check if array size will be exceeded
// should more questions be added
if ((iNumQuestions + 1) > iMAX_QUESTIONS)
{
gotoxy (iLEFT_MARGIN, iERROR_LINE);
cout << cERR_ARRAY_MAX_MSG;
}
else
{
// intialise question number to the first available
// empty record
iQuestionIndex = iNumQuestions;
// But in case, a question has been deleted, find the
// first available question number by looking
// for the first deleted questions
for (int i = 0; i< iNumQuestions; i++)
{
if (stQuestionArray[i].iStatus == DELETED)
{
iQuestionIndex = i;
break;
}
}
// input new question information and store in the
array
stQuestionArray[iQuestionIndex] =
stAddNewquestion(iQuestionIndex + 1);
iNumQuestions++;
}
}
else
{
if (iNumQuestions < 1)
{
gotoxy (iLEFT_MARGIN, iERROR_LINE);
cout << cERR_NO_QUESTIONS_MSG;
}
else
{
switch (cOption)
{
case cMODIFY_QUESTION_OPTION:
// modify selected questions
vModifyQuestion(stQuestionArray);
break;
case cDELETE_QUESTION_OPTION:
// delete selected question
vDeleteQuestion(stQuestionArray);
iNumQuestions--;
break;
case cGENERATE_QUESTIONS_OPTION:
// display all questions directing the output to a
file
vGenerateQuestions(stQuestionArray, iNumQuestions);
break;
}
}
}
// vOutputArray(stQuestionArray, iNumQuestions);
vPause();
// redisplay menu and accept new option
cOption = cMenu();
}
iActualNumQuestions =
iDetermineLastActiveQuestionNum(stQuestionArray,
iMAX_QUESTIONS);
//cout << "Number of questions before writing to file
are:" << iActualNumQuestions << endl;
//getch();
// Write all questions to file before exiting the
program
iQuestionsWritten = vWriteFile (stQuestionArray,
iActualNumQuestions);
// Output number of questions written to file
gotoxy (iCOL_POS, iEND_LINE - 4);
cout << iQuestionsWritten;
if (iQuestionsWritten == 1)
cout << " question ";
else
cout << " questions ";
cout << "saved to file.";
gotoxy (iCOL_POS, iEND_LINE - 2);
cout << cGOODBYE_MESSAGE;
vPause();
}
/*******************************************************************************
Name: vOutputArray
Purpose: To output each record stored in the array.
This function is used for
debugging purposes only.
Pass: Reference to the question array (array of
structs)
Size of array (int)
Return: none
*******************************************************************************/
void vOutputArray(stQuestion stQuestionArray[], int
iArraySize)
{
cout << "Array elements are : \n\n";
for (int i = 0; i < iArraySize; i++)
{
cout << stQuestionArray[i].iQuestionNumber << ". "
<< (stQuestionArray[i].cQuestion) << endl
<< stQuestionArray[i].cAnswer << endl
<< stQuestionArray[i].iDifficultyLevel << endl
<< stQuestionArray[i].iStatus << endl << endl;
}
}
/*******************************************************************************
Name: vInitialiseQuestions
Purpose: To initialise each record stored in the array
with a question number,
empty string for questions, space for answer, and -1
for status.
Pass: Reference to the question array (array of
structs)
Size of array (int)
Return: none
*******************************************************************************/
void vInitialiseQuestions(stQuestion
stQuestionArray[], int iArraySize)
{
for (int i = 0; i < iArraySize; i++)
{
stQuestionArray[i].iQuestionNumber = (i+1);
strcpy(stQuestionArray[i].cQuestion, " ");
stQuestionArray[i].cAnswer = ' ';
stQuestionArray[i].iDifficultyLevel = 0;
stQuestionArray[i].iStatus = INITIAL;
}
}
/*******************************************************************************
Name: iReadquestionFile
Purpose: To read every record on the question file and
store them in an array
Pass: The array into which to place the question
information
Return: A count of the number of records placed in the
array
*******************************************************************************/
int iReadQuestionFile(stQuestion stQuestionArray[])
{
ifstream inQuestionFile;
int iQuestionNum = 0;
stQuestion stQuestionRecord;
// open the question file
inQuestionFile.open(cTESTBANK_FILE_NAME);
// if the file opened OK get the data from it
if (!inQuestionFile)
{
gotoxy (iLEFT_MARGIN, iERROR_LINE);
cout << cERR_FILE_OPEN_MSG;
}
else
{
// read the initial record
stQuestionRecord =
stReadQuestionRecord(inQuestionFile);
// read all records and store into the array
while (!inQuestionFile.eof())
{
// increment and assign question number to record
stQuestionRecord.iQuestionNumber = iQuestionNum + 1;
// copy question record to array
stQuestionArray[iQuestionNum] = stQuestionRecord;
iQuestionNum++;
// this will ignore any characters in the file like \n
etc
// and wont cause problems with reading the next
record
inQuestionFile.ignore();
// read the next record
stQuestionRecord =
stReadQuestionRecord(inQuestionFile);
}
// close file after reading
inQuestionFile.close();
}
return iQuestionNum;
}
/*******************************************************************************
Name: iReadquestionRecord
Purpose: To read one record from the question file
Pass: Address of the question file
Return: The record read
*******************************************************************************/
stQuestion stReadQuestionRecord (ifstream
&questionFile)
{
stQuestion stQuestionRecord;
questionFile.getline(stQuestionRecord.cQuestion,
iQUESTION_LEN + 1);
questionFile >> stQuestionRecord.cAnswer;
questionFile >> stQuestionRecord.iDifficultyLevel;
stQuestionRecord.iStatus = ACTIVE;
return stQuestionRecord;
}
/*******************************************************************************
Name: cMenu
Purpose: To display the main menu and input and
validate the users selection.
The number of questions read from file is displayed
only if the
menu is initially displayed.
Pass: If this is the intitial display (boolean)
Number of questions read from file (int)
Return: The entered option (char)
*******************************************************************************/
char cMenu(bool bIfInitial, int iNumQuestions)
{
char cOption;
clrscr();
cout << setiosflags(ios::left);
gotoxy (iLEFT_MARGIN, iHEADING_LINE);
cout << '\t' << cMAIN_HEADING << endl << endl;
for (int i = 0; i < iMENU_ITEMS; i++)
{
// output menu items for a constant string
cout << '\t' << cMENU[i];
if ((i == 0) || (i == 1) || (i == (iMENU_ITEMS - 2)))
cout << endl << endl;
else
cout << endl;
}
// only outputs number of questions for the intitial
menu display
if (bIfInitial)
{
gotoxy(iCOL_POS, iEND_LINE - 2);
cout << iNumQuestions;
if (iNumQuestions == 1)
cout << " question ";
else
cout << " questions ";
cout << "in system.";
}
// input and validate option
cOption = cInputLetter(iOPTION_LINE,
cVALID_MENU_OPTIONS, cERR_MENU, cMANDATORY);
return cOption;
}