Hello everyone
I have been trying to learn the greater mysteries of linked lists and have come across an error that is inexplicable to me at my current level of understanding.
I have written a program that attempts to form a linked list to store student information in the form of a struct. This code is a modified version of an earlier program that I wrote that implemented the same functionality using arrays. The program reads the data from a file StudentRecords.txt, then allows the user to search it, display all the data, or add or delete a student record, as well as writing any changes made back into the file
The primary issue is with the DisplayDatabase and LoadDatabase functions, which work as intended the first time the user displays the database, but prints all of the information in the file again after the first set when it is called a second time.
Thus, if the user selects (4) from the menu once, then again selects (4), the information will be printed twice the second time.

Here is the code. I am sorry that it is so long, but I felt that I should post all of it, since I am not sure what exactly is causing the problem.

#include <iostream>
#include <string>
#include <fstream>
#include <cmath>
#include <iomanip>
#include <stdio.h>


using namespace std;

int check = 0;   // Input validation statement..
int limit = 99;

struct StudentRec  // Structure to define variables in the Student Records.
{
    string Name;                        // String data for the Name of studnet.
    string ID;                         // String name for the ID # of the student.
    double GPA;                       // Define GPA of students.
    bool compare(StudentRec valid)   // Validate data
    {
        if(Name == valid.Name && ID == valid.ID && GPA == valid.GPA) // If statment to validate data
            return true;                                            // If correct, return true
                return false;                                      // If incorrect, return false.
    }
};

struct StudentListNode
{
StudentRec StudentData;
StudentListNode *next;
};

StudentListNode*startpoint=NULL;



void LoadDatabase() //load the txt to array
{
    bool check = false;
    StudentListNode *temp, *temp2, *temp3;
    StudentRec student;
    string full, nmlst, nmfst,id, nm;  //variables needed for reading in
    double gpa;                        //
    ifstream file;
    ofstream create;                     //
    file.open("StudentRecords.txt");  //read the file
    if (!file)
	{                      //error output
        cerr << "Can't open input file " << endl;
        create.open("StudentRecords.txt");
        create.close();
	}
    while(file >> nmfst >> nmlst >> id >> gpa)        //for to read the correct loop
    {
        check = false;
        temp = new StudentListNode;
		nm = nmlst + ", " +nmfst;
        student.Name = nm;
        student.ID = id;
        student.GPA =  gpa;
        temp -> StudentData = student;
        temp -> next = NULL;
        if(startpoint == NULL)
        {
            startpoint = temp;
        }
        else
       {
           temp2 = startpoint;
         // We know this is not NULL - list not empty!
         while (temp2->next != NULL)
           {
               temp3=temp2->next;
               if(((temp2->StudentData.Name) < (temp->StudentData.Name)) && ((temp3->StudentData.Name) > (temp->StudentData.Name)))
               {
                   temp2->next = temp;
                   temp->next = temp3;
                   check = true;
                   break;
               }
               else
               {
                   temp2 = temp2->next;
               }
              // Move to next link in chain
           }
           if(temp2->next == NULL && !check)
           {
               temp2->next = temp;
           }

       }
    }
    file.close();
}
    void print_a_record(StudentRec show)
{
    // Display data in orderly and readable fashion.  Left center data.
    cout
    << left << setw(32) << show.Name
    << setw(10) << show.ID
    << setw(7) << fixed << setprecision(2) <<  show.GPA << endl;
}
void Displaydatabase()
{
    StudentListNode*temp;
    int x=0;
    temp = startpoint;
    cout << endl;
    if (temp == NULL)
    {
        cout << " No Student Record" << endl;
    }
    else
    {
        cout << right << "No. " << setw(15) << "Name: " << setw(31) << "ID: " << setw(10) << "GPA\n"
        << "------------------------------------------------------------------ \n";
        while (temp != NULL)
        {
            if (x<9)
            {
                cout << x+1 << ". " << setw(10) << " ";
            }
            else if (x<100)
            {
                cout << x+1 << ". " << setw(9) << " ";
            }
            else
            {
                cout << x+1 << ". " << setw(8) << " ";
            }
                print_a_record(temp-> StudentData);
                temp = temp->next;
                x++;
        }
    }
}

// Function to be called so a , is not implimented in the text file when its not needed.
string convertName(string name)
{
    string nmlst, nlst;
    int count =0;
    for (int x = 0; x < name.size(); x++)
    {
        if (count ==0)
        {
            if (name[x] != ' ' && name[x] != ', ')
            {
                nlst +=name[x];
            }
            else count ++;
        }
        else
        {
            if(name[x] != ', ')
            nmlst += name[x];
        }
    }
    name = nmlst + " " + nlst;
    return name;
}



// Function to call menu and display information to user.




// Function to add a record to the file.
void addsrecord ()

//Function apple >>> 0002

{
    StudentListNode *temp, *temp2, *temp3;
    temp = new StudentListNode;
    string last,first,name,id;double gpa;
    string gpatest;                      //to test gpa making sure it is int
    StudentRec student;                      //temporary student
    cout << "What is the last name?\n";
    cin >> last;
    cout << "What is the first name?\n";
    cin >>first;
    name = last +", " +first;            // add name last, first format
    cout << "What is the ID?\n";
    cin >> id;                        // get the gpa
    cout << "What is the gpa?\n";
    cin >> gpa;

    student.Name = name;                //set everything
    student.ID = id;
    student.GPA = gpa;
    temp->StudentData = student;
    temp->next = NULL;
    temp2 = startpoint;

    if(startpoint == NULL)
    {
        startpoint = temp;
    }
    else
    {
        while (temp2->next != NULL)
       {
           temp3=temp2->next;
           if(((temp2->StudentData.Name) <= (temp->StudentData.Name)) && ((temp3->StudentData.Name) > (temp->StudentData.Name)))
           {
               temp2->next = temp;
               temp->next = temp3;
               break;
           }
           else
           {
               temp2 = temp2->next;
           }
          // Move to next link in chain
       }
       if(temp2->next == NULL)
       {
           temp2->next = temp;
       }
    }
}

// Function that allows user to deltee a student record by entering their M #.
void delsrecord ()
{
// Variables.
//Student chosen >>>>>>> 0005
string id;
StudentListNode *temp, *temp2;
temp = startpoint;
if(startpoint == NULL)
    cout << "There are no students" << endl;
else
{
    cout << "What is the ID?? " << endl;
    cin >> id;
    if(id == temp->StudentData.ID)
    {
        cout << "Record was delteed" << endl;
        startpoint = startpoint->next;
        delete temp;
    }
    else
    {
        temp = startpoint; temp2 = temp->next;
        if(temp->next == NULL)
        {
            cout << "Student can't be found!!" << endl;
        }
    else
    {
        while ((temp2->next !=NULL) && (temp2->StudentData.ID != id))
        {
            temp = temp->next;
            temp2= temp->next;
        }
        if(temp2->StudentData.ID == id)
        {
            temp->next = temp2->next;
            cout << "Record has been deleted" << endl;
            delete temp2;
        }
    }
    }
}
}

// Function to find a record within the file. Using a bianary search.
void findsrecord()
{
    string input;
    cout << "Enter the ID number of the student you wish to find (M########) " << endl;
    cin >> input;
    // Loop to compare strings for the search.
    while (startpoint!=NULL)
    {
        if(startpoint->StudentData.ID == input)
        {
        cout << "Student has been found." << endl;
        cout << "Name: " << startpoint->StudentData.Name << endl << "GPA: " << startpoint->StudentData.GPA << endl <<
        "ID: " << startpoint->StudentData.ID << endl;
        return;
        }
         // cout << count << limit << endl;

        // If the number is not found in the file.
        if (startpoint == NULL)
        {
            cout << "Can't find student!" << endl;
        }
        else
        {
            startpoint = startpoint->next;
            continue;
        }
    }
}


// Function that saves the information passed through the array to the .txt file.
void save()
{
    ofstream file;
    file.open("StudentRecords.txt");
    string temper; // A convertName for students[p].Name
    // Command to pass all data to file from array.
    while((startpoint != NULL))
    {
        temper = convertName(startpoint->StudentData.Name);
        file << temper<< " " << startpoint->StudentData.ID << " " << startpoint->StudentData.GPA << endl;
        startpoint = startpoint -> next;
    }
    file.close();
}

void pickChoice()		//Gives user choices in program.
{
    cout << "********************************************************************************" << endl;
	cout << "\t\t\t(1) Add a student record." << endl;
	cout << "\t\t\t(2) Delete a student record." << endl;
	cout << "\t\t\t(3) Find a student's information." << endl;
	cout << "\t\t\t(4) Display all information in the database." << endl;
	cout << "\t\t\t(5) Exit program." << endl;
	cout << "********************************************************************************" << endl;
}
// Void statement to display menu to user.
void displayMenu()
{
	bool temp = true;
    //LoadDatabase();
    while(temp != false)
    {
		pickChoice();
		int choice;
		cin >> choice;
		while(!cin.good() || choice < 1 || choice > 5)
		{
			cin.clear();
			cin.ignore( 80, '\n' );
			cout << endl << "Invalid entry. Enter an integer 1 - 5: " << endl;
			pickChoice();
			cin >> choice;
		}
		switch(choice)
		{
			case 1:	LoadDatabase();
					addsrecord();
					save();
				break;
			case 2:	LoadDatabase();
					delsrecord();
					save();
				break;
			case 3:	LoadDatabase();
					findsrecord();
				break;
			case 4:	LoadDatabase();
					Displaydatabase();
				break;
			case 5:	LoadDatabase();
				exit(1);
				break;
			default:cout << "Enter a valid choice please." << endl;
				break;
		}
	}
}

int main()
{
	displayMenu();			//Display menu
	return 0;
}

If anyone has any insights on what could be causing this error, they would be greatly appreciated.

Thanks
ace8957

Recommended Answers

All 7 Replies

>>Thus, if the user selects (4) from the menu once, then again selects (4), the information will be printed twice the second time.

The reason is that the linked list is not destroyed between calls to menu item (4). Thus, when that function is called again it simply appends all the records in the file to the end of the current linked list.

The fix for that is to make sure the linked list is destroyed at the beginning of LoadDatabase() function.

Also, you have no code to exit the loop?!? Secondly, after the switch try to set choice to -1 or something to make sure the input is changed (sometimes the input gets stuck when at EOF)

thanks a bunch guys for the help. When I do something like this I feel so incompetent.

I have run into another issue. I done as you recommended and written a function to destroy the list, which I right before every time I call LoadDatabase(). the program appears to work correctly the first time the Display function is called, but crashes when it is called a second time.

Does anyone know why this could be happening?

OK, I just copy-pasted your code into a new .cpp file in VS2008 to confirm that you do not have any specific class here. What you might want to do is make a student class, move the lines 33-319 inside the class and to try to work with it from there. The reason i believe is that you are just doing function calls to global functions. you are working with nodes and usually with linked list they should be part of a class.
Example

give it a shot because you might be trying to access restricted memory and that might be crashing it.

You could always post the function used to delete the database so we could look at it.

Why do you load the database for each choice and save it to file everytime you change the data? Unless there's a reason to do otherwise keep the data in RAM as long as possible and read/write from file as infrequently as possible. In your case I would only load the database once in displayMenu(), like it looks like you tried at one point, and write the data to file at the end of the program (say inc case 5: just before you exit() the program, thus terminating the loop) or, in a more general situation, once the user is done with this file's information and wants to use another.

My latest rework of the above code has taken into account all of the suggestions that everyone has been so helpful with. However, it still does not work. At present, I call the LoadDatabase function to create the linked list only once (inside of main, to avoid confusion). A class did not seem to help. I am pretty sure I did something else wrong. Further, though there is a problem with my function to destroy the list, it should not be needed in my current build, since it only calls the LoadDatabase function once, and there is no reason to have to rebuild it.

I believe that I have the issue narrowed down to something with either my LoadDatabase function or my delsrecord() function to delete a student record. The issue that I keep encountering is that (1)the insertion sort does not work correctly, for reasons unbeknownst to me, and (2)the delsrecord function somehow messes up the startpoint, since DisplayDatabase prints the error message saying there are no student records, and that the list is empty.

The present version of LoadDatabase() looks like this:

void LoadDatabase() //load the txt to array
{
    bool check = false;
    StudentListNode *temp, *temp2, *temp3;
    StudentRec student;
    string full, nmlst, nmfst,id, nm;  //variables needed for reading in
    double gpa;                        //
    ifstream file;                     //
    file.open("StudentRecords.txt");  //read the file
    if (!file) {                      //error output
        cerr << "Can't open input file " << endl;
        exit(1);
        }
    while(file >> nmfst >> nmlst >> id >> gpa)        //for to read the correct loop
    {
        check = false;
        temp = new StudentListNode;

         nm = nmlst + ", " +nmfst;
        student.Name = nm;
        student.ID = id;
        student.GPA =  gpa;
        temp -> StudentData = student;
        temp -> next = NULL;
        if(startpoint == NULL)
        {
            startpoint = temp;
            //current = startpoint;
            cout << temp->StudentData.Name << "\n\n";
        }
        else
       {
           temp2 = startpoint;
         // We know this is not NULL - list not empty!
         while (temp2->next != NULL)
           {
               temp3=temp2->next;
               if(((temp2->StudentData.Name) <= (temp->StudentData.Name)) && ((temp3->StudentData.Name) > (temp->StudentData.Name)))
               {
                   temp2->next = temp;
                   temp->next = temp3;
                   cout << temp->StudentData.Name << "\n\n";
                   check = true;
                   break;
               }
               else
               {
                   temp2 = temp2->next;
               }
              // Move to next link in chain
           }
           if(temp2->next == NULL && !check)
           {
               cout << check << "\n\n";
               temp2->next = temp;
               cout << temp->StudentData.Name << "\n\n";
           }

       }
    }
    file.close();
}

AND the current version of delsrecord() looks like this:

void delsrecord ()
{
// Variables.
//Student chosen >>>>>>> 0005
string id;
StudentListNode *temp, *temp2;
temp = startpoint;
if(startpoint == NULL)
    cout << "There are no students" << endl;
else
{
    cout << "What is the ID?? " << endl;
    cin >> id;
    if(id == temp->StudentData.ID)
    {
        cout << "Record was delteed" << endl;
        startpoint = startpoint->next;
        delete temp;
    }
    else
    {
        temp = startpoint;
        temp2 = temp->next;
        if(temp->next == NULL)
        {
            cout << "Student can't be found!!" << endl;
        }
    else
    {
        while ((temp2->next !=NULL) && (temp2->StudentData.ID != id))
        {
            temp = temp->next;
            temp2= temp->next;
        }
        if(temp2->StudentData.ID == id)
        {
            temp->next = temp2->next;
            cout << "Record has been deleted" << endl;
            delete temp2;
        }
        else
        {
            cout << "Student not found" << endl;
        }
    }
    }
}
}

Any thoughts would be much appreciated. Thanks to everyone for bearing with me on this.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.