You are here:

C++/Storing Info in Files

Advertisement


Question
Hey Ralph, hope all is well...

I plan to take official C++ classes in July this year. Seeing as it has been my weakness in the past, I decided to try out a little bit myself just to get ready for the whole   thing. I've purchased a green book, "Problem Solving with C++" and I've read it up and am trying to do the projects in it myself. Thing is, some of the projects in the book have a solution given, but there is this one problem revolving around storing information in files, which I cannot get around. I'll let you see the problem.

It asks us to make a program (using files) that would let a person enter student deatils as follows:

1 > Add student Details
2 > View a student detail
3 > Edit a student's details
4 > Delete a student record
5 > Display all students' grades
6 > Display all students' marks
7 > Display class average and standard deviation
8 > Save a student detail to a file
9 > Exit

I've decided to try to do this problem, step by step (otherwise I get confused), and so I started out with let's say the user picking option 1, to enter student details. Here is my code thusfar.

___________________________________________________________
#include <iostream>
#include <cmath>
#include <string>
#include <fstream>
using namespace std;
int main ()
{
  int user_input_number;
  double student_id;
  string last_name;
  string first_name;
  int counter;
  int number_of_students;
  ofstream student_name("information.dat");

  cout << "Welcome to course marks maintenance" << endl;
  cout << "Please enter a number corresponding to its     function" << endl;
  cout << "1 > Add student Details" << endl;
  cout << "2 > View a student detail" << endl;
  cout << "3 > Edit a student's details" << endl;
  cout << "4 > Delete a student record" << endl;
  cout << "5 > Display all students' grades" << endl;
  cout << "6 > Display all students' marks" << endl;
  cout << "7 > Display class average and standard deviation" << endl;
  cout << "8 > Save a student detail to a file" << endl;
  cout << "9 > Exit" << endl;
  cin >> user_input_number;

if (user_input_number == 1)
{
  cout << "You have chosen to add student details" << endl;
  cout << "We require you to input 15 details per student" << endl;
  cout << "Please tell us how many students' info you are about to enter" << endl;
  cin >> number_of_students;
  cout << "You are kindly requested to enter every student's name" << endl;

  if (student_name.is_open())
     {
       for (counter=0; counter<=number_of_students; counter++)
       {
         cout << "Enter student last name ==>";
         getline (cin, last_name);
         student_name << last_name << endl;
         cout << "Enter student first name ==>";
         getline (cin, first_name);
         student_name << first_name << endl;
       }
   }
   
  student_name.close();

}
system ("pause");
return 0;
}
__________________________________________________________

Simply put, this program was supposed to work like this: if I entered "1", it would ask me to enter student details, and I wrote code till where I could enter student first and last names. I run the program and it does work, but I have a runtime error. I'll show you a sample output of what I got.

_______________________________________________________

when it enters the place where i key in student names (I chose 2 students), if gives this as output

Enter student last name ==> Enter student first name ==>
Enter student last name ==> (i can give input here)
Enter student first name ==> (i can give input here)
Enter student last name ==> (i can give input here)
Enter student first name ==> (i can give input here)

I wanted to ask, why does it initially repeat this line without any prompt of input?

"Enter student last name ==> Enter student first name ==>"

How can I fix it? What would also be a good efficient way for me to finish the rest of my program? Should I use files or should I use arrays and loops instead? Any tips? Sample program? Ofcourse I don't expect you to write it all for me, I just need a 'kick start'

Thanks alot in advance.

Regards,
Yanling

Answer
Well it is because of the previous input request for the number of students. You read it using:

   cin >> number_of_students

All well and good but it stops reading the input stream at the end of line character so the first item read by the next:

   getline (cin, last_name);

is the end of line from the previous input, which has the effect of causing getline to think it has an empty line already buffered so it consumes the end of line character without waiting and returns.

The fix is obvious - use a dummy call to getline for example:

   getline (cin, last_name); // reads previous end of line
   getline (cin, last_name); // reads last name entered by user

I cannot help much with the best way to proceed as I do not know what you have learnt so far nor what the question requires. However here are some points about your code.

As I do not know the book you are using I have no idea how much C++ you have covered, so if you have not covered any of the topics mentioned then you might like to peek ahead or just note the ideas as interesting!

First, it seems to me that using a few functions and maybe a switch statement would be a useful  technique to create cleaner code:

   switch (user_input_number)
   {
   case 1:
       status = AddStudent( <arguments> );
       break;

   case 2:
       // ...

   default:
       // Handle case for invalid action number
   }

You will have to think about passing data around between main and the worker functions however. I did not think about what sort of data this might be in the example and just put the placeholder <arguments> where the actual argument values would go. I have assumed the action functions such as AddStudent return some value to indicate success or failure (a bool value) or maybe even success or an error code (an integer value). I assume the bool or integer status variable is defined elsewhere. You could go further and define a specific enumerated type for the values entered:

   enum UserStudentActions
        { AddStudent = 1
        , ViewStudentDetails

        // ...

        };

  // ...

  cout << AddStudent << " > Add student Details" << endl;

  cout << ViewStudentDetails << " > View a student detail" << endl;

  // ...

   switch (user_input_number)
   {
   case AddStudent:
       status = AddStudent( <arguments> );
       break;

   case ViewStudentDetails:
       // ...

   default:
       // Handle case for invalid action number
   }

My next point is that you are not checking to see if the input stream cin is in a good state after each input. If it is not then you will need to take action. If the action is recoverable (i.e. the program can continue) then the error state on the stream will have to be cleared. You should see if your book has any information on the IOStream status flags and functions that operate on them.

Oh and if you have covered user defined types (classes and structs) then you might consider defining a Student type that can contain all the information for a student and knows how to save (and load) itself from file as well as other operations such as accessing student details like first and last name as well more complex data such as their grades and marks.

Finally, I do not think you need to save the details to file until the use requests such an action so why open the information.dat file now?

As far as I can tell in this example you only need to save individual student data on request and never load it back into the program. So you will not have to open a file until the user selects the "Save a student detail to a file" option. At this point you should use a file name related to the student selected and will have to think about issues such as the file already exists or cannot be opened. The easiest solution to the former problem is to use the default open file for writing action of create if it does not exist and open and truncate (i.e. delete existing data) if it does exist.

This would tend towards using a collection (or collections if not using a class or struct for the student data) in memory for the student data and only writing to file when requested by the user to do so. However, I would go back and carefully read the question again just to make sure.

If I were doing something like this in a more realistic way I would ensure all student data is written to file once entered that is adding a new student would end in it being saved. Of course you might have to let the user review the data before writing it to file and they may need to edit it if there are any mistakes. This would seem to require a different program structure to that of your problem.

Other functions may always read the data from file or they may work on cached data in memory which is written back to file if modified (again after the user is happy with the changes).

Why do I wish to work in this way? Well RAM is volatile. If there is a power cut or the program or OS crashes then the more (modified) data that is in memory that has not been saved the more work the user will loose. So if the user enters 20 student records, then edits then reviews and edits them one at a time then saves them one at a time the procedure is firstly very tedious for the user and secondly open to loosing data until the last record has been saved.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.