Solved

Reading and Writing files

Posted on 1998-05-18
41
539 Views
Last Modified: 2008-02-26
Greetings,

I have a database program designed to allow student entry, searching, deleting etc.  One thing I need is the ability to read a file into the database.

Below is the code I have and which works but when I try to read a file of 100 entries, the whole file doesn't read in, only about 25.

void Database::readDatabase()
{

      char buff[100];
      char fname[40];
      struct find_t ffblk;
      int done;

      cout<<"Choose a file from drive A for reading
               to databse:\n\n";

            //  show directory
            printf("Directory listing of *.*\n");
            done = _dos_findfirst
                       ("*.*",_A_NORMAL,&ffblk);
            while (!done)
            {
            printf("  %s\n", ffblk.name);
                  done = _dos_findnext(&ffblk);
            }
            cin>>fname;
            ifstream input(fname);
            if (!input)
                  {cout<<"File doesn't exist!\n";
                  return;}

            while(input.getline(buff,100)!=0)
            {
            if(strlen(buff)==0)exit(-1);
              Student *tmp = new Student(buff);
            addStudent(tmp);

            }  cout<<"File read to database"; return;

}

The call to new Student(buff) & addStudent(tmp) dynamically creates and adds a student to the database.  There is plenty of memeory on my system so that's not the problem.

Thanks for the help!

0
Comment
Question by:John500
  • 19
  • 11
  • 5
  • +2
41 Comments
 
LVL 11

Expert Comment

by:alexo
ID: 1163024
Hmmm...

istream::getline() reads a line terminated by a '\n'.
Make sure that no line in the file is longer than 97 characters (100 - carriage-return, line-feed and terminating zero).

Change
  while(input.getline(buff,100)!=0)
to
  while(input.getline(buff, sizeoff buff).good())

See if it helps.
0
 

Author Comment

by:John500
ID: 1163025
Alexo,

Glad you could grab this question.

Ok, I replaced that line as you said but still no difference.

Could the problem have anything to do with the file extension being .txt which I'm reading in?

This is a sample of the lines being read into the database:

7868342, Clinton, 00, 0.00, U.S., has a problem with inhaling
2542341, Dick. V., 92, 2.00, U.S., Annoyingly repeats "Baby"
9953891, babson,23, 2.3, alabama, a great guy
9953886, baden, 12, 4, wyoming, nice person

As you can see, each line is not very large.

You may also want to look at the Student::Student constructor that makes each student.  I just sent that to you via e-mail because it will probably more readable.  Does this type of transmission scrable things when I send them?

I don't think the constructor is the problem but to eliminate all doubt...  Also, the addStudent function called in the readDatabase function above, alphabetizes each entry.  I'll send that if you think you need to see it.

Standing by, John
0
 
LVL 11

Expert Comment

by:alexo
ID: 1163026
John, I wouldn't have time to check it today.  I suggest you reopen the question for other experts.
0
 

Author Comment

by:John500
ID: 1163027
Alex,

Will do as you suggested, thanks for the help thus far.

Anyone out there interested in this question?

John
0
 
LVL 3

Expert Comment

by:Norbert
ID: 1163028
Do you have empty lines in your file?
while(input.getline(buff,100)!=0)
{
if(strlen(buff)==0)exit(-1);  // this will terminate if you have an empty line
         Student *tmp = new Student(buff);
addStudent(tmp);

}
Hope that helps
 Norbert
0
 

Author Comment

by:John500
ID: 1163029
If I have empty lines in my file, and the code below causes termination for empty lines, is there a way to over come these empty lines?
 
while(input.getline(buff,100)!=0)
{
if(strlen(buff)==0)exit(-1);  // this will terminate if you have an empty line
         Student *tmp = new Student(buff);
addStudent(tmp);

}

0
 

Author Comment

by:John500
ID: 1163030
How could I make the following line make sense so that getline continues until the end of the file?

while(input.getline(buff,100)!eof(input))
0
 
LVL 3

Expert Comment

by:Norbert
ID: 1163031
Yes, of course
what about
while(input.getline(buff,100)!=0)
{
if(strlen(buff)!=0)
{
          Student *tmp = new Student(buff);
          addStudent(tmp);
}

}
0
 
LVL 22

Expert Comment

by:nietod
ID: 1163032
How about the following?

while(input.getline(buff,100) != 0)
 {
      if(strlen(buff) != 0)
     {
         Student *tmp = new Student(buff);
         addStudent(tmp);
    }
}


This causes the program to skip empty lines (not use them), but to continue processing when it encounters them.
0
 
LVL 3

Expert Comment

by:Norbert
ID: 1163033
perhaps you should use the endof file function and change your code to
while(!input.eof())
{
     input.getline(buff,100);
     if(strlen(buff)!=0)
     {
               Student *tmp = new Student(buff);
               addStudent(tmp);
      }
}

0
 
LVL 22

Expert Comment

by:nietod
ID: 1163034
Norbert, is there an advantage in using the eof() function?  (I don't use the C++ file I/O.)  I thought that the return vlaue from getline() could be used just as well.
0
 
LVL 11

Expert Comment

by:alexo
ID: 1163035
Hi Todd, welcome back.  Did she get it?

< lecture mode on >

An ios has three state bits:
  ios::eofbit - End of file reached.
  ios::failbit - A possibly recoverable formatting or conversion error.
  ios::badbit - A severe I/O error.

ios::eof() checks ios::eofbit.  ios::operator void*() and ios::operator!() check the other two.

< lecture mode off >

0
 

Author Comment

by:John500
ID: 1163036
Todd,

The lines you gave will help I'm sure, but it didn't fix the problem.  Below is the code I'm using for the n)ames/notes option. If you agree nothing looks wrong, how about suggesting a way to print the _next and _previous pointers of each student that is being added.  I thinking that the pointer logic might be messed up for each student added.

I've tried cout<<pNew->_next<<pNew->_prev; but the compiler gives me "undefined symbol..."  I don't know why this would happen.  I insert directly after a student is entered.

I've changed my mind, I'll send the code via e-mail.

John


0
 
LVL 22

Expert Comment

by:nietod
ID: 1163037
Alex,
     Yes, she got it.  She had already defended, this was just the graduation.  Food, Family and wine.  Especially wine.

     What does getline() return though?  I thought it was the EOF status.  Is it all three state bits?
0
 
LVL 3

Expert Comment

by:Norbert
ID: 1163038
Nietod,
Alexo told you someting about the bits
I had a look to the online help of vc++ 4.20 and I saw

basic_istream& getline(E *s, streamsize n, E delim = T::newline());
The unformatted input function extracts up to n - 1 elements and stores them in the array beginning at s. It always stores T::eos() after any extracted elements it stores. In order of testing, extraction stops:
·      at end of file
·      after the function extracts an element that compares equal to delim, in which case the element is neither put back nor appended to the controlled sequence
·      after the function extracts is.max_str() elements
 
If the function extracts no elements, it calls setstate(failbit). In any case, it returns *this.

so getline returns always *this and that is probably always !FALSE
So I think there is a big advantage :-)


0
 

Author Comment

by:John500
ID: 1163039
Thanks all for the input.

As I said in the last letter, the eof routine will work well if the data is messed up, but I've made sure the file is clean.

The n)ames/notes menu item still only prints about 25 out of 100

John
0
 

Author Comment

by:John500
ID: 1163040
I forgot to mention, the routine I'm using to make sure the file is being read is:

...
while(!input.eof())
{
      input.getline(buff,100);
      if(strlen(buff)!=0)
      {
      printf("line %02d = [%s]\n",counter,buff);
      Student *tmp = new Student(buff);
      addStudent(tmp);
      counter++;
      }
    }  cout<<"File read to database"; return;
}

I've also printed _numberStudents after each time the new Student constructor is called which tells me all 100 students have been created.

It has to be either the addStudent func or the printNames func

John
0
 
LVL 11

Expert Comment

by:alexo
ID: 1163041
Cograts!

>> What does getline() return though?
The stream reference of course!  Which is implicitly convertable using the two operators I mentioned above.

0
 

Author Comment

by:John500
ID: 1163042
Todd,

Anything to report??

0
 

Author Comment

by:John500
ID: 1163043
Open to Alex
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 

Author Comment

by:John500
ID: 1163044
Open to Alex, please check your e-mail.

John
0
 

Author Comment

by:John500
ID: 1163045
Todd,

Please check your mail

John
0
 
LVL 11

Expert Comment

by:alexo
ID: 1163046
>> Open to Alex, please check your e-mail.

Several things:

First, although I appreciate your vote of confidence, I suggest you keep the question open for everybody until you get your solution.

Second, I'm in a different time zone from you (7 hours before the east coast).  You cannot expect to find me online in "normal" (for you) hours.

Third, send me the whole project as a zipped attachment, including the input file and I'll take a look at it.
0
 
LVL 3

Expert Comment

by:Norbert
ID: 1163047
I would like to see the code too.
because I live in germany I am also in a different time zone

0
 

Author Comment

by:John500
ID: 1163048
To whom it may interest:

For a doubly linked list, do insertions to the head or tail automatically set the necessary pointers (3) and how about insertions to the middle and their pointers (4).

The following code is not alphabetizing my student entries.  I believe the entries are unlinked once created:

void Database::addStudent(Student *pNew)
{
      Student *p = _head;
      Student *NxtPtr = _head;
      Student *PrvPtr = NULL;

            while (p)
            {
            if(p->_id == pNew-> _id)
            {cout<<"Duplicate ID, can't be entered!\n";
                return;}

            p = p->_next;
            }

        while (NxtPtr)  // While there are students to test.
      {
      if (strcmp(NxtPtr->_name,pNew->_name) > 0)
        break;         // stop the search.
                                          
      PrvPtr = NxtPtr;       //  Save -> current student.
      NxtPtr = NxtPtr->_next;
      }

      if (PrvPtr != NULL)
      {PrvPtr->_next = pNew;
      _numStudents++; return;}

        else if (PrvPtr == 0)
       {      _head = pNew;
            _numStudents++; return;}

      else if (NxtPtr != NULL)
            NxtPtr->_prev = pNew;
            pNew->_next = NxtPtr;
            pNew->_prev = PrvPtr;
            _numStudents++; return;
}    // end of addStudent
0
 
LVL 22

Expert Comment

by:nietod
ID: 1163049
The problem is you don't want the returns in there!  if you move the _numstudents++ to the end (you only want it once) and remove the retuns (or put one at the end) you will be fine.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1163050
Actually, that's not 100% true.  You put some bad "elses" in too.  Why did you mess with it?  It should be

while (p)
{
   if(p->_id == pNew-> _id)
   {
      cout<<"Duplicate ID, can't be entered!\n";
      return;
   }
   p = p->_next;
}

while (NxtPtr)  // While there are students to test.
{
   if (strcmp(NxtPtr->_name,pNew->_name) > 0)
      break;    // stop the search.
   PrvPtr = NxtPtr;       //  Save -> current student.
   NxtPtr = NxtPtr->_next;
}
if (PrvPtr != NULL)
  PrvPtr->_next = pNew;
else
   _head = pNew;
if (NxtPtr != NULL)
   NxtPtr->_prev = pNew;
pNew->_next = NxtPtr;
pNew->_prev = PrvPtr;
 _numStudents++;

Note, as you said was a requirement, that sets 3 or 4 pointers every time.
Alex, or anyboady out there.  Do you agree that this is correct?
0
 

Author Comment

by:John500
ID: 1163051
How can you say you are meeting the pointer requirements??
..."Note, as you said was a requirement, that sets 3 or 4 pointers every time..."

The only place you are setting pointers is when a student is inserted in the middle.  The head and tail are not automatic!

0
 

Author Comment

by:John500
ID: 1163052
...Even when you have set the pointers you left out
PrvPtr->_next = pNew.  It isn't automatic either!!
0
 

Author Comment

by:John500
ID: 1163053
Further more,

One occurence of _numStudents at the end is not good.  What happens if no students are entered for some reason?  The number of students will still be incremented.  The increments should take place in the area the student is entered.

As far as returns go, they should be entered where I put them in.  Why stay in the function when the job has been done??
0
 
LVL 22

Expert Comment

by:nietod
ID: 1163054
Do you keep a pointer to the tail?  if so that is not being set, although that is a small change (2 lines).  

The head is being set. when it needs to, namely when a new first item is inserted.  When a new first item is inserted, PrvPtr will be NULL,  (Because it was initialized to NULL and was never reset in the loop)  In that case the _head pointer is set after the loop ends.

Don't take my word for it!  Follow in your mind (or on paper) what happens when you (a) add to a totally empty list and (b) add an item that comes before the first item already in the list.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1163055
You wrote
>>  Why stay in the function when the  job has been done??
and at the same time you are complaining that the job isn;t getting done (that pointers aren't be set in every case.)

The problem is that the job isn't done at those places you've put in the returns!  The code has to run to the end to get done.  

Trust me.  I've written this sort of thing a couple thousand times in a dozen different languages.  Don't assume I'm wrong and try to change it to what seems right.  Assume I'm right and try to test it in your mind.
0
 

Author Comment

by:John500
ID: 1163056
Granted,

You don't have to set the _head->_prev pointer, it is Null by default but you do have to set _head->_next or it will have a default of Null also!  And the _head->_next->_prev must be set.

Same goes with the tail, only the pNew->_prev has to be set and the pNew->_prev->_next pointer.  That is the way it is referenced or by your second pointer PrvPtr->_next = pNew.

Let's put it this way, you were right about for loop being bad, but the instructor's method of pointers was at least getting 25 items in the list, not six.
0
 

Author Comment

by:John500
ID: 1163057
Concerning the job being done,

If the while loop is terminated and comes to:

if (PrvPtr != NULL)

and the PrvPtr is Null this condition is passed by, No?  If PrvPtr isn't Null then it isn't passed by and it is executed, No?  If it is executed then a student is entered, No?  If a student is entered then there is no reason to stay in the function.  That goes for any other condition that is entered into and which also ends in a student being entered, the whole function should be done, no other jobs to do.

Are you saying that a condition is executed even if it isn't true?  For (PrvPtr != NULL)?? is this entered if it isn't true??

Your set up wouldn't compile because of a misplaced else statement by-the-way.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1163058
You are soooooo confussed.  I have no idea what to say.

(I will say this, the else was placed right in the code above.  I'm not sure why it didn't compile.)

I'm going to take a stab at your problem.  Do you know that in the code

if (NxtPtr != NULL)
    NxtPtr->_prev = pNew;
pNew->_next = NxtPtr;

that the last line ("pNew->_next = NxtPtr") will always be executted?  It is not part of the "if".  only the middle line is part of the "if"?  Do you understand that?  

If that is not you missunderstanding.... give me a call (405) 844-8647.
0
 

Author Comment

by:John500
ID: 1163059
Todd,

Congrats!!

The reason the code wouldn't compile is because you didn't put parens around the if or the else statements.

Once I did that, all 100 students were entered in the database!

My problem is with things done behind the scene.  To me, if an "if" statement is false, than NOTHING under it or within its parenthesis, should be executed.

Anyway, good job!  Talk tomorrow



0
 
LVL 22

Expert Comment

by:nietod
ID: 1163060
If an "if" is false only the 1st statement after it is not executed.  If that statement is a compound statmenet (a set of braces ({  }) then all the statements withing that compound statement will be skipped.  However execution will resume after the statemetn folowing the "if".

When it comes to executing the stuff in the the parenthesis.  That's a little tricky.  If there is an expression in the parenthesis at least part of the expression will be executed and in some cases all of the expression will be executed.  The compiler will execute as much it needs to in order to determine whehor or not  the the "if" should run.  for example if you have

if (F1() || F2())

The compiler will call F1 first.  If F1 returns true, then the statmenet in the if should run, so it does not call F2.  If F1 returns false, then there still is a possibility that the if should run, depending on F2's return value.  So in that case it calls F2

If you have

if (F1() && F2())

It calls F1 first (always goes left to right)  If F1 returns false, then the "if" should not run.  In that case there is no point in calling F2, so it doesn't. If F1 returns true, the "if" might still run depending on F2's return values, so it does call F2.

As far as the missing parens causing compile problems.  I do not see any missing parens in the sample above.  It is possible that there were ones in the e-mail.  That is my most common typo!  (I'm a pascal programmer mascerading as a C++ programmer.)
0
 

Author Comment

by:John500
ID: 1163061
Todd,

Sorry not to write sooner, but my wife went into false labor last night around 3:30.  We got back from the hospital mid morning and I've been sleeping ever since.

The above clarification on what is and is not executed will help.  I still need to study that procedure to see why it is working.  Here is the portion of code I put parenthesis around:

if (PrvPtr != NULL)
  {PrvPtr->_next = pNew;} // added parens
else
   {_head = pNew;} // added parens

if (NxtPtr != NULL)
  NxtPtr->_prev = pNew;
pNew->_next = NxtPtr;
pNew->_prev = PrvPtr;
 _numStudents++;

I cleaned up a couple of other functions in my program last night like the a)ge function and something else (just in case you saw how terrible it was).

I am going to open up a new question now on the delete function, after I study the addStudent one some more.  Who knows it may come to me but I've been having trouble figuring all those different scenarios out also.  The delete is kind of tricky (swaping pointers and such).

Make this question available for grading and I'll do it.  Thanks again for the help!!

0
 
LVL 22

Accepted Solution

by:
nietod earned 50 total points
ID: 1163062
Those aren't parenthesis, those are braces, and they are unnecessary.  You can combine two or more statements into a single compound statement using braces.  This is important because for loops, while loops, if statements etc all work on only one statement.  If you want more than one statement controlled by these things, you use a compound statement.
0
 
LVL 84

Expert Comment

by:ozo
ID: 1163063
Unnecessary, but helpful to ease maintenance if you ever want to change the statement(s)
0
 
LVL 22

Expert Comment

by:nietod
ID: 1163064
Well now we're getting into a matter of style....but I think braces are the worst things in the world--other than liver--so I don't use them if I don't have to.  I think they make C and C++ too hard to read.
0

Featured Post

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now