This is my first time using pointers and I'm having some trouble. So far I have declared a struct named Student which is meant to hold the data for each student. There is an input file with an unknown amount of entries. Each entry has a first and last name along with 7 scores.

I am trying to read in the data from the text file at this point. Then I will need to sort the data (selection sort) in alphabetical order by the students last names. And lastly output the data to a new text file.

Below I'll provide what I've done so far. Really I'm just trying to get a better insight as to how I should be approaching this problem. I can figure out the sorting and output later.

A sample entry from the input file:

ANTHONY CLARK  83  85  84  85  82  85  78
#include <fstream>
#include <iostream>

using namespace std;

struct Student
{
    char First[30];
    char Last[30];
    float Score1, Score2, Score3, Score4, Score5, Score6, Score7;

};

int main()
{
    ifstream infile;
    ofstream outfile;
    int size=5;
    infile.open("input.txt");
    
    struct Student * data;
    data = new struct Student[size];

    for (int i = 0; i < size; i++ )
    {
        infile.getline(data[i].First,30);

    }


    


    infile.close();
    outfile.open("output.txt");
    outfile.close();
     



    delete [] data;
    

    return 0;
}

Do not use getline here. The first name can be UP TO 30 characters, but will usually be less than that. Your getline stores other fields in the first name. This input file is separated by spaces and newlines, so it is tailor made for the >> operator instead of getline. I see no need to read in the data using getline.

for (int i = 0; i < size; i++ )
    {
        infile >> data[i].First >> data[i].Last >> data[i].Score1 >> /* same for the rest */
    }

Thanks for the help. That is makes things much clearer. My next question is: How does the array continue to expand in order to span the length of the input file? I just did a simple print to the output file and it only displays the first 5 names on the list. I know that is because I set size = 5. What should I have done to make sure the array will continue to allocate memory?

#include <fstream>
#include <iostream>

using namespace std;

struct Student
{
    char First[30];
    char Last[30];
    float Score1, Score2, Score3, Score4, Score5, Score6, Score7;

};

int main()
{
    ifstream infile;
    ofstream outfile;
    int size=5;
    infile.open("input.txt");
    
    struct Student * data;
    data = new struct Student[size];

    for (int i = 0; i < size; i++ )
    {
        infile >> data[i].First >> data[i].Last >> data[i].Score1 >> data[i].Score2
               >> data[i].Score3 >> data[i].Score4 >> data[i].Score5
               >> data[i].Score6 >> data[i].Score7;
    }


    


    infile.close();
    outfile.open("output.txt");

    for (int i = 0; i < size; i++ )
    {
    outfile << data[i].First << data[i].Last << " " << data[i].Score1
            << " " << data[i].Score2 << " " << data[i].Score3 << " "
            << data[i].Score4 << " " << data[i].Score5 << " "
            << data[i].Score6 << " " << data[i].Score7 << endl;
    }


    outfile.close();
     



    delete [] data;
    

    return 0;
}

Thanks for the help. That is makes things much clearer. My next question is: How does the array continue to expand in order to span the length of the input file? I just did a simple print to the output file and it only displays the first 5 names on the list. I know that is because I set size = 5. What should I have done to make sure the array will continue to allocate memory?

You have a few options.

  1. Pick an original size that you know will be large enough to handle anything you'll encounter (say 1,000,000) and size it to that initially. It's a waste of memory and bad coding practice, but resizing will be unnecessary. That's assuming that such a maximum exists in the first place.
  2. Use a vector or something else that does the expanding and contracting for you.
  3. Go through the file once and count how many records there are and size the array to that, then go through again to read the data.
  4. Do an initial guess of the size, like you do with 5. Start reading the file. When you run out of room, resize the array manually by declaring a new array of the new size, deep copy the elements from the first array to the new array, then release the memory of the original array and reassign any pointers.
  5. Use malloc, calloc, realloc, and free rather than new and delete in order to possibly avoid a deep copy - this is a C solution rather than a C++ solution.

I would imagine you are expected to implement approach 4 above. Here' a link where Narue comments.

http://www.daniweb.com/forums/thread13709.html

There are other examples too. Google "C++ resize dynamic array" or something similar.

Yea your correct. I need to dynamically expand the array as needed. I took a look at the link you gave me (thanks by the way) and am still having trouble implementing it correctly. So far this is what i have:

#include <fstream>
#include <iostream>

using namespace std;

struct Student
{
    char First[30];
    char Last[30];
    float Score1, Score2, Score3, Score4, Score5, Score6, Score7;

};

int main()
{
    ifstream infile;
    ofstream outfile;
    int size = 5;
    infile.open("input.txt");
    
    struct Student * data;
    struct Student * temp;
    data = new struct Student[size];
    

    for (int i = 0; i < size; i++)
    {
        infile >> data[i].First >> data[i].Last >> data[i].Score1 >> data[i].Score2
               >> data[i].Score3 >> data[i].Score4 >> data[i].Score5
               >> data[i].Score6 >> data[i].Score7;

        
        
    }
    
    temp = new struct Student[size * 2];

        for ( int i = 0; i < size; i++ )
        {
            temp[i] = data[i]; 
         }

        size *= 2;
        data = temp;

    for (int i = 0; i < size; i++)
    {
        infile >> temp[i].First >> temp[i].Last >> temp[i].Score1 >> temp[i].Score2
               >> temp[i].Score3 >> temp[i].Score4 >> temp[i].Score5
               >> temp[i].Score6 >> temp[i].Score7;
    }



    infile.close();
    outfile.open("output.txt");

    for (int i = 0; i < size; i++ )
    {
    outfile << data[i].Last << ", " << data[i].First << " " << data[i].Score1
            << " " << data[i].Score2 << " " << data[i].Score3 << " "
            << data[i].Score4 << " " << data[i].Score5 << " "
            << data[i].Score6 << " " << data[i].Score7 << endl;

    }

    delete [] data;
    delete [] temp;
    outfile.close();
     
    return 0;
}

Define "still having trouble implementing it correctly". What is happening that shouldn't be happening or what isn't happening that should be happening?

I am unsure as to where I should be placing size *= 2. Its purpose is to double the size of the array each time it is necessary.

At first, I didn't know whether I should be placing the new array (temp) inside the loop which is pulling data from the input file. The link you posted was helpful. I am just having trouble applying it to my program correctly. The program as it is now will now display 10 items, which isn't the end product I am looking for.

What I would like to do is have this new array named temp to continue and allocate memory until the input file has been accounted for entirely. Am I on the right track by setting up the new temp array after the original for loop, and running a series of them on its own? Thanks again for your help.

Give this a try.

ifstream infile;
ofstream outfile;
int size = 1;
    
infile.open("input.txt");
    
struct Student * data;
struct Student * temp;
data = new struct Student[size];

int i = 0;
while( !infile.eof() )
{
	infile >> data[i].First >> data[i].Last >> data[i].Score1 >> data[i].Score2
		>> data[i].Score3 >> data[i].Score4 >> data[i].Score5
		>> data[i].Score6 >> data[i].Score7;
        
	if( !infile.eof() )
	{
		size++, i++;
		temp = new struct Student[size];
		for( int c = 0; c < size; c++ )
		{
			temp[c] = data[c];
		}
		data = temp;
	}
}

infile.close();

I know this is constantly swapping between temp and data but its semi compact and gives the output I think you are looking for.

I am unsure as to where I should be placing size *= 2. Its purpose is to double the size of the array each time it is necessary.

At first, I didn't know whether I should be placing the new array (temp) inside the loop which is pulling data from the input file. The link you posted was helpful. I am just having trouble applying it to my program correctly. The program as it is now will now display 10 items, which isn't the end product I am looking for.

What I would like to do is have this new array named temp to continue and allocate memory until the input file has been accounted for entirely. Am I on the right track by setting up the new temp array after the original for loop, and running a series of them on its own? Thanks again for your help.

Don't confuse size with capacity. Size is the number of actual elements. Capacity is the number of elements you have room to store. size must always be less than or equal to capacity. It's usually less than capacity.

So you need a capacity variable and a size variable. "Resizing" involves changing the capacity, not the size. Obviously, "resize" can lead to confusion. Feel free to call your variables arrayCapacity and numStudents , so it will be extra clear. I would in fact advise doing that to avoid confusion.

So any display must involve the "size" variable. Any "resizing" involves changing the "capacity" of the array.

Give this a try.

Thanks! This is much more efficient and gets exactly at what I'm trying to do.

Don't confuse size with capacity. Size is the number of actual elements. Capacity is the number of elements you have room to store. size must always be less than or equal to capacity. It's usually less than capacity.

So you need a capacity variable and a size variable. "Resizing" involves changing the capacity, not the size. Obviously, "resize" can lead to confusion. Feel free to call your variables arrayCapacity and numStudents , so it will be extra clear. I would in fact advise doing that to avoid confusion.

So any display must involve the "size" variable. Any "resizing" involves changing the "capacity" of the array.

Alright I got it. Thanks again.


I should be set for now. I'm going to work on implementing maybe a selection sort to output the data in alphabetical order. Thanks again guys.

I'm trying to add a recursive quicksort function to display the names in alphabetical order. In the code I'm only trying send the last names for each entry to be compared. Is this the right idea? Does anything stick out that needs to be fixed?

#include <fstream>
#include <iostream>
#include <cstring>

using namespace std;

struct Student
{
    char First[30];
    char Last[30];
    float Score1, Score2, Score3, Score4, Score5, Score6, Score7;

};

void Sort(char LastName[], int Length); 
 
void QuickSort(char LastName[], int Left, int Right);

int main()
{

ifstream infile;
ofstream outfile;
int numstudents = 1;
    
infile.open("input.txt");
    
struct Student * data;
struct Student * temp;
data = new struct Student[numstudents];


int i = 0;
while(!infile.eof())
{
	infile >> data[i].First >> data[i].Last >> data[i].Score1 >> data[i].Score2
		>> data[i].Score3 >> data[i].Score4 >> data[i].Score5
		>> data[i].Score6 >> data[i].Score7;
        
	if(!infile.eof())
	{
		numstudents++, i++;
		temp = new struct Student[numstudents];
		for( int c = 0; c < numstudents; c++ )
		{
			temp[c] = data[c];
		}
		data = temp;
	}
}
     infile.close();



    infile.close();
    outfile.open("output.txt");
    
    
    Sort(data[i].Last, numstudents);

    
    float score;
    for (int i = 0; i < numstudents - 1; i++ )
    {
            outfile << data[i].Last << ", " << data[i].First << "\t\t\t\t\t";
            
            score = (data[i].Score1 + data[i].Score2 + data[i].Score3 +
            data[i].Score4 + data[i].Score5 + data[i].Score6 +
            data[i].Score7) / 7;

            outfile << score << "\t\t\t\t";

            if (score >= 90)
            {
                outfile << 'A' << endl;
            }
            else if (score >= 80)
            {
                outfile << 'B' << endl;
            }
            else if (score >= 70)
            {
                outfile << 'C' << endl;
            }
            else if (score >= 60)
            {
                outfile << 'D' << endl;
            }
            else
            {
                outfile << 'F' << endl;
            }

           

    }

    delete [] data;
    delete [] temp;
    outfile.close();
     
    return 0;
}

void Sort(char LastName[], int Length) 
{ 
  QuickSort(LastName, 0, Length-1); 
} 
 
void QuickSort(char LastName[], int Left, int Right)  
{  
  int i, j;  
  char x, y;  
  
  i = Left; j = Right;  
  x = LastName[( Left + Right) / 2 ];  
  
  do {  
    while((LastName[i] < x) && (i < Right)) 
       i++;  
    while((x < LastName[j]) && (j > Left)) 
       j--;  
  
    if(i <= j) {  
      y = LastName[i];  
      LastName[i] = LastName[j];  
      LastName[j] = y;  
      i++; j--;  
    }  
  } while(i <= j);  
  
  if(Left < j) 
     QuickSort(LastName, Left, j);  
  if(i < Right) 
     QuickSort(LastName, i, Right);  
}

You have written

infile.close();

two times.

At the top of the outfile.open() section you have

Sort(data[i].Last, numstudents);

I think you are thinking of putting that into a for() loop or something to call that function once per person in the list.

If you are going to do a self made sort then I would send the whole list into 1 function.

example

void Sort(Student data[], int size)
{
	for( int c = 0; c < size; c++ )
	{	
		int index = c;
		for( int i = c; i < size; i++ )
		{
			if( data[index].Last[0] > data[i].Last[0] )
			{
				index = i;
			}
			if( index != c && i == size-1 )
			{
				Student temp;
				temp = data[index];
				data[index] = data[c];
				data[c] = temp;
			}				
		}
	}
}

This sorts the last name by the 1st letter not based off the whole last name.

The potential problem here is that they won't be in complete order since the list is 250+ entries. Are there any modifications to your code I can make to take the entire name into account? Also, for some reason the last entry is being cut out of the output file. Any clear reason for this? Thanks again, you've been an excellent help.

I'll check about the modification but the reason why the last guy gets cut off is because you have numstudents-1 in the for() loop for output.

Don't use the return value of eof() in controlling the loop. Sooner or later that syntax will result in creating a second copy of the last entry in the file. I haven't found a very readable explanation of why, so I don't post references. I recommend doing something like this:

while(infile >> data[i].First)
{
   infile >>  data[i].Last >> data[i].Score1 >> data[i].Score2
           >> data[i].Score3 >> data[i].Score4 >> data[i].Score5
		>> data[i].Score6 >> data[i].Score7;

or this:

while(infile >> data[i].First >>  data[i].Last >> data[i].Score1 >> data[i].Score2 >> data[i].Score3 >> data[i].Score4 >> data[i].Score >> data[i].Score6 >> data[i].Score7)
{

and someday you'll be able to make it even more readable by overloading the >> operator for the struct type you declared so you can write this:

while(infile >> data[i])
{

sfuo is probably correct about why the last one is cut out though.

What happened to doubling the capacity of data each time you reached maximum capacity instead of doing what you're doing? The current syntax you are using creates significant memory leak since every time you assign temp to data you loose the ability to access the memory that data was pointing to.

Since the name fields in the struct are C style strings you will need to compare them with the strcmp(), or similar, function in order to compare two names for lexical (in)equality. I believe strcmp() and related functions are in the cstring header file. You could use the equals operator to compare name fields if they were declared as STL string objects instead of C style strings.

Edited 7 Years Ago by Lerner: n/a

Yeah I haven't played around with the cstring lib and I use strings instead of char arrays so I read what Lerner said below and changed the sort() function.

void Sort(Student data[], int size)
{
	for( int c = 0; c < size; c++ )
	{
		int index = c;
		for( int i = c; i < size; i++ )
		{
			if( strcmp(data[index].Last, data[i].Last) > 0 )
			{
				index = i;
			}
			
			if( index != c && i == size-1 )
			{
				Student temp;
				temp = data[index];
				data[index] = data[c];
				data[c] = temp;
			}			
		}
	}
}

This works for what I've tested.
Don't forget to #include <cstring>

And I changed the infile while() loop to what Lerner suggested too

while( infile >> data[i].First >> data[i].Last >> data[i].Score1 >> data[i].Score2
		>> data[i].Score3 >> data[i].Score4 >> data[i].Score5
    		>> data[i].Score6 >> data[i].Score7)
{
	if( !infile.eof() )
	{
		size++, i++;
		temp = new struct Student[size];
		for( int c = 0; c < size; c++ )
		{
			temp[c] = data[c];
		}
		data = temp;
	}
}

Thanks guys! The program runs great. Your help is very much appreciated.

Edited 7 Years Ago by thisisanfield.1: n/a

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