Hello,

I'm a college student taking a programming class and I'm trying to write a program for this class. I've done most of the work already I just need some hints or a push in the right direction as far as my client-code is concerned. Here's the code for the various .CXX and .H files. Any help is appreciated. I'm not asking for a solution just some hints.

ItemType.h

#include <iostream>
#include <fstream>
#include <iomanip>

using namespace std;

const int MAX_ITEMS = 10;
enum RelationType {LESS, EQUAL, GREATER};

class ItemType
{
public:
        RelationType ComparedTo(ItemType) const;
        void GetItemFromFile(ifstream&);
        void WriteItemToFile(ofstream&) const;
        void WriteInvalidItemToFile(ofstream&) const;
        bool invalidItem(ItemType) const;

private:
        int id;
        float gpa;
        char major[2];
};

ItemType.cxx

#include "ItemType.h"

void ItemType::GetItemFromFile(ifstream& inFile)
// PURPOSE:
// INPUT:
// PRE:
// OUTPUT:
// POST:
// NOTE:
{
        inFile >> id >> gpa >> major[0] >> major[1];
}

void ItemType::WriteItemToFile(ofstream& outFile) const
// PURPOSE:
// INPUT:
// PRE:
// OUTPUT:
// POST:
// NOTE:
{
        outFile << id << setw(20) << gpa << setw(20) << major[0] << major[1];
}

bool ItemType::invalidItem(ItemType item) const
// PURPOSE:
// INPUT:
// PRE:
// OUTPUT:
// POST:
// NOTE:
{
        return(item.id >= 111 && item.id <= 999 &&
               item.gpa >= 0.0 && gpa <= 4.0 &&
               item.major[0] == 'C' || item.major[0] == 'I' &&
               major[1] == 'S');
}

void ItemType::WriteInvalidItemToFile(ofstream& outFile) const
// PURPOSE:
// INPUT:
// PRE:
// OUTPUT:
// POST:
// NOTE:
{
        outFile << id << " " << gpa << " " << major[0] << major[1];
}

RelationType ItemType::ComparedTo(ItemType otherItem) const
// PURPOSE:
// INPUT:
// PRE:
// OUTPUT:
// POST:
// NOTE:
{
        if(id < otherItem.id)
                return LESS;
        else if(id > otherItem.id)
                return GREATER;
        else return EQUAL;
}

UnsortedType.h

#include  "ItemType.h"

class  UnsortedType
{
public:
        UnsortedType();
        void MakeEmpty();
        bool IsFull() const;
        int LengthIs() const;
        void RetrieveItem(ItemType& item, bool& found);
        void InsertItem(ItemType item);
        void DeleteItem(ItemType item);
        void ResetList();
        void GetNextItem(ItemType& item);

private:
        int length;
        ItemType info[MAX_ITEMS];
        int currentPos;
} ;

UnsortedType.cxx

#include  "UnsortedType.h"

UnsortedType::UnsortedType()
{
  length = 0;
}

void UnsortedType::MakeEmpty ( )
// Pre:  None.
// Post:  List is empty.
{
   length = 0 ;
}

bool UnsortedType::IsFull () const
// Pre:  List has been initialized.
// Post:  Function value == (list is full).
{
   return (length == MAX_ITEMS);
}

int UnsortedType::LengthIs() const
// Pre:  List has been inititalized.
// Post:  Function value == (number of elements in list).
{
   return length ;
}

void UnsortedType::RetrieveItem(ItemType& item, bool& found)
// Pre:  Key member of item is initialized.
// Post:  If found, items key matches an elements key in the list and a copy
//        of that element has been stored in item; otherwise, item is unchanged.
{    bool moreToSearch;
     int location = 0;

     found = false;
        moreToSearch = (location < length);
        while (moreToSearch  &&  !found)
        { switch (item.ComparedTo(info[location]))
          {       case  LESS    :
                  case  GREATER : location++;
                                  moreToSearch = (location < length);
                                  break;
                  case  EQUAL   : found = true;
                                  item = info[location] ;
                  case  EQUAL   : found = true;
                                  item = info[location] ;
                                  break ;
          }
        }
}

void UnsortedType::InsertItem(ItemType item)
// Pre:  List has been initialized. List is not full. item is not in list.
// Post:  item is in the list.
{
        info[length] = item ;
        length++ ;
}


void UnsortedType::DeleteItem(ItemType item)
// Pre:  items key has been inititalized.
//       An element in the list has a key that matches items.
// Post:  No element in the list has a key that matches items.
{
     int location = 0;

     while (item.ComparedTo(info[location]) != EQUAL)
        location++;

        // move last element into position where item was located

        info[location] = info[length - 1] ;
        length-- ;
}


void UnsortedType::ResetList()
// Pre:  List has been inititalized.
// Post:  Current position is prior to first element in list.
{
   currentPos = -1;
}

void  UnsortedType::GetNextItem(ItemType& item)
// Pre:  List has been initialized. Current position is defined.
//       Element at current position is not last in list.
// Post:  Current position is updated to next position.
//        item is a copy of element at current position.
{
   currentPos++;
   item = info[currentPos];
}

runList.cxx (This is my driver/client-code)

#include "UnsortedType.h"

int main()
{
        ifstream inFile;
        ofstream outFile;
        char listOperation;
        int length = 0;

        inFile.open("in.data");
        outFile.open("out.data");
        if(inFile.fail() || outFile.fail())
        {
                cout << "Input or output file opening failed" << endl;
        }

        UnsortedType rdList;
        ItemType item;

        outFile << "<~~~~~~~ Grade Report ~~~~~~~>" << endl;

        rdList.MakeEmpty();
        inFile >> listOperation;

        while(inFile)
        {
                item.GetItemFromFile(inFile);
                if(listOperation == 'A')
                        rdList.InsertItem(item);
                else if(listOperation == 'D')
                        rdList.DeleteItem(item);
                inFile >> listOperation;
        }

        rdList.ResetList();
        length = rdList.LengthIs();

        for(int count = 1; count <= length; count++)
        {
                rdList.GetNextItem(item);
                if(!item.invalidItem(item))
                {
                        item.WriteInvalidItemToFile(outFile);
                        outFile << "  **** invalid exam score" << endl << endl;
                        rdList.DeleteItem(item);
                }
        }

        rdList.ResetList();
        length = rdList.LengthIs();

        outFile << "STUDENT ID" << setw(20) << "GPA" << setw(20) << "MAJOR" << endl;

        for(int count = 1; count <= length; count ++)
        {
                rdList.GetNextItem(item);
                item.WriteItemToFile(outFile);
                outFile << endl;
        }

        outFile << "> > > end < < <" << endl;

        return 0;
}

Here is a sample of my input

A 444 3.33 CS
A 777 2.75 IS
A 1000 3.21 CS
A 222 2.88 IS
D 222
A 666 3.54 IS
A 999 3.25 CS

Here is a sample of my output

<~~~~~~~ Grade Report ~~~~~~~>
1000 3.21 CS  **** invalid exam score

STUDENT ID                 GPA               MAJOR
444                3.33                   CS
777                2.75                   IS
> > > end < < <

Here is what I would like my output to be

<~~~~~~~ Grade Report ~~~~~~~>
1000 3.21 CS  **** invalid exam score

STUDENT ID                 GPA               MAJOR
444                3.33                   CS
777                2.75                   IS
666                3.54                   IS
999                3.25                   CS
> > > end < < <

In the Input file the char 'A' adds the item to the list while the char 'D' deletes the item from the list.

Forgive the unfinished and sloppy code...still working on cleanliness :)

The problem description is:

Create an unsorted list for student id (111-999), GPA (0.0-4.0), and major (two chars either "CS" or "IS") as ItemType of the Unsorted List. Do not add invalid data to the list, for invalid data print the record with a message at the beginning of output file.

Again, I'm not looking for an answer just some hints. I'm just trying to figure out where my logic is wrong.

Thanks in advance.

[edit]Oh, I forgot to add that the UnsortedType.h and UnsortedType.cxx source was given to me and is perfect and shouldn't have much of an effect on my client-code. I just posted it so you can see the whole picture. However the problem just basically wants me to write my own ItemType.cxx, ItemType.h, and runList.cxx code.[/edit]

First thing, get rid of those floats, it's a pain in the backside. Use double instead. Also forget char arrays, you're in c++, so might as well use std::strings, there's less chance of overflow.

Start putting a load of couts in your header files to aid debugging. Or if you're lucky some mo fo will come along and do it for you.

bool UnsortedType::IsFull () const
// Pre:  List has been initialized.
// Post:  Function value == (list is full).
{
   return (length == MAX_ITEMS);
}

Bool, would have to return true or false, so what's going on up there? Also check your file handling is without bugs. (not checked it myself...but just saying)

Thanks for your reply. I do appreciate it.

Anyways, like I said I'm a college student and my prof. expects it done a certain way. Believe me I would rather use doubles and strings but this is how he wants it done.

I will be sure to add the couts to see where things are going awry.

Like I said, everything associated with the UnsortedType class (it's member functions and its data) are what he gave us to use. We can't modify it in anyway. I couldn't tell you why but that is what he wants. The only thing that I can modify and change are ItemType.cxx, ItemType.h, and runList.cxx. I'm pretty sure the ItemType files are right. I just think that there's a problem in my client-code (runList.cxx).

My immediate guess would be the line:

D 222

Is causing your program to exit prematurely. Probably because this:

void ItemType::GetItemFromFile(ifstream& inFile)
// PURPOSE:
// INPUT:
// PRE:
// OUTPUT:
// POST:
// NOTE:
{
        inFile >> id >> gpa >> major[0] >> major[1];
}

...is expecting another two more variables, which it will still try an attempt to read.

Yeah, thats actually right. I added values for the gpa and major variables into my "in.data" file and it worked perfectly. So what I did is I made a new member function. Here's the new member function.

void ItemType::GetDeleteItemFromFile(ifstream& inFile)
// PURPOSE:
// INPUT:
// PRE:
// OUTPUT:
// POST:
// NOTE:
{
        inFile >> id;
}

I also modified my client-code just a little. Here's the modified code.

while(inFile)
        {
                if(listOperation == 'A')
                {
                        item.GetItemFromFile(inFile);
                        rdList.InsertItem(item);
                }
                else if(listOperation == 'D')
                {
                        item.GetDeleteItemFromFile(inFile);
                        rdList.DeleteItem(item);
                }
                inFile >> listOperation;
        }

However, even in this setup if my "in.data" is not modified it still messes up. So I'm just looking for a way that after reading 'D', then the id number it should go to the next line in the data file for the next read cycle.

Here's how I see it. Your class infrastructure:-

private:
        int id;
        float gpa;
        char major[2];

Will ONLY work if your lines contain those exact three variables. Obviously, your
teacher has given you the line "D 222" to test your little programming skills.

The only way to overcome this, is make sure that any lines that do not contain these three variables, are discarded straight away. I.e. they don't even make it through to your class infrastructure.

What you ideally need to be doing, as well, is to be reading your file in LINE by LINE. You are NOT doing this at the momentf.

The worst problem with your code is how you have ASSUMED each line will always follow this particular structure:-

inFile >> id >> gpa >> major[0] >> major[1];

Bad, let's say the first word ain't an integer, as you have stated the id to be, what do you think's gonna happen? It's gonna crash.

Thus, I would read the file each line at a time.


Then, each line would be represented by a string of characters. This way it can't possibly crash. Then I would split the line into parts using the whitespace as a delimiter.

If the parts, match the description:-

id (int) >> gpa (float) >> major(char[])

And only three variables are found, then I would pass it into the class. Job done.

If you need help how to read in lines, and splitting each line into parts using the whitespace as a delimiter - ask.

Thanks again for your replies.

As I stated above I have fixed the problem I was originally having. I just added an extra emember function which would only read the id and nothing else.

The only thing I'm struggling with now is getting the output in the correct order.

Here's a sample of my current output:

<~~~~~~~ Grade Report ~~~~~~~>
1000 3.21 CS  **** invalid exam score

          STUDENT ID                 GPA               MAJOR
                 444                3.33                   CS
                 777                2.75                   IS
                 999                3.25                   CS
                 666                3.54                   IS

> > > end < < <

The student id entry 666 should come right after the id entry 777 and entry 999 should be last. Unfortunately as you can see this is not the case. Again I believe that it may be solved in my client-code.

Ok, I have figured out my problem and I'm pretty much finished except I have to add documentation to the source code. Thanks for your help guys.

This article has been dead for over six months. Start a new discussion instead.