Solving Vector Length Test Problem: EOF & Do-While Loops

  • Thread starter Thread starter yungman
  • Start date Start date
  • Tags Tags
    Vectors
Click For Summary
The discussion centers around issues with a C++ program that uses vectors to store employee information but experiences input problems within a do-while loop. The user encounters an issue where the program skips prompts for last names after the first entry, which is attributed to unhandled input buffer issues. Suggestions include using `cin.ignore()` to clear the input buffer after reading user input, which resolves the skipping problem. Additionally, there is a desire to implement EOF detection for future enhancements, with advice given to check the `eofbit` flag when reading from files. The conversation also touches on the potential use of iterators and class structures for a more scalable design in the user's project.
  • #31
yungman said:
BUT that heavily involve R/W to hard disk or flash memory, it will be very slow. I think it's better to do all those in RAM first and copy one time to file.

Whoa! Slow down!

First, you want to get your code working first before deciding it's too slow. Getting a wrong answer faster is no real virtue.

Second, you have no idea whether your code is too slow until you run it. Your records are 88 bytes long, so a modern disk can read in two million of these records per second.

Finally, you have no idea where your code is too slow until you run it. Spending time optimizing one part can be a total waste if it's some other part slowing you down.

PS If you are planning on using a bubble sort, it is unlikely that disk read time will be your biggest concern.
 
Last edited:
Technology news on Phys.org
  • #32
yungman said:
imagine a bear is chasing you, whether you are ready or not, you run!
Those that can tell their story are those that know:
[*] Bears run faster than people
[*] Bears have very poor eyesight for stationary objects, rather better for things that move
 
  • #33
Just speaking as a programmer, when things don't behave as expected, have you tried debugging? Reviewing the values of all variables at each program step? When things are acting strangely, debugging can quickly lead you to the actual problem, it may not be what you think is causing the problem.
 
  • #34
I have been working on the program, yes, I am still crawling in C++, but after I worked on the program, the program is not exactly running material. I say it's at best slow walk. I manage to using the Constructor in the .h file to check for file existence, if not, create an empty file. In main, I have option to create the new file or read back the existing names.

This is a much simplified version that each name structure has only name and phone number. I can create as many name as I want in the directory. So far, the option is to read them all back. Displaying particular name, add new name or delete name will come later. But the structure of the program is done already. Doing the sorting, adding, deleting should be quite easy. In the whole C++, the sorting so far is the easiest part for me. So I am not worry about it.

Of cause, this is the first time I wrote such a complicate program, I am sure it can be improved quite a bit. Any suggestion is very welcome. I want to push what I learn so far, this program sure did pushed me. I spent a lot of time searching on line to look at how people do things also. Here is what I have. It's is NOT a big program so far.

I still believe in doing as few interfacing with the file as possible. Not only it's slower, hard disk and flash memory have finite working cycles. I don't want to do all the searching, sorting stuff directly on the file. What I do is download the entire file to the vector DirV in the Constructor, the rest of the program is just working on the DirV until closing the program. The Destructor then write the entire content of the vector DirV back into the file. So one read and one write to the file every time I open the program.Here is the .h file, I use inline function instead of creating a separate implementation file:
C++:
#ifndef VectorCall_H
#define VectorCall_H
#include <vector>
#include <fstream>
class VectorCall
{
  private:
    const static int nameL = 25, phoneL = 12;
  public:
    struct Directory { char name[nameL];    char phone[phoneL]; };
    std::vector<Directory>DirV;
    Directory Dir; bool newF;
    std::fstream file;

    VectorCall()//Constructor read file to DirV
    {
        file.open("Directory.dat", std::ios::in | std::ios::binary);
        if (file.fail())
        {//If file doesn't exist, create the file
          std::cout << " File does not exist";
          file.open("Directory.dat", std::ios::out|std::ios::binary|std::ios::app);
          file.close();
          newF = true;
        }
        else
        {//If the file exist, read entire file into vector DirV.
          file.read(reinterpret_cast<char*>(&Dir), sizeof(Dir));
          do
          {
            DirV.push_back(Dir);
            file.read(reinterpret_cast<char*>(&Dir), sizeof(Dir));
          } while (!file.eof());
          file.close();
        }
    }
    ~VectorCall()//Distructor
    { int ct2 = 0;
      file.open("Directory.dat", std::ios::out | std::ios::binary);
      do//write the updated data in vector DirV into the file before closing
      { file.write(reinterpret_cast<char*>(&DirV[ct2]), sizeof(DirV[ct2]));
        ct2++;
      }while(ct2 < DirV.size());
      file.close();
    }
    void push_backV() { DirV.push_back(Dir); }
    void writeFile(){}
    void print() { std::cout << "Name:  " << Dir.name <<
            "   Phone:  " << Dir.phone << "\n\n"; }
};
#endif

This is the main .cpp file:
C++:
#include <iostream>
#include <string>
#include <vector>
#include "VectorCall.h"
using namespace std;

int main()
{
  char more;  int index = 0; const int nameL = 25, phoneL = 12;
  VectorCall Info;
  char yes;
  if (Info.newF == true)
  {//First time set up the directory by entering all the names and info
     cout << " Welcome to the Directory program. You need to enter informations:\n\n";

    do
    {
        cout << " Enter name:  "; cin.getline(Info.Dir.name, nameL);
        cout << " Enter phone:  "; cin.getline(Info.Dir.phone, phoneL);
        //Info.print();
        Info.push_backV();//writing into vector DirV.
        cout << " Do you want to enter another name?  "; cin.get(more);
        cin.ignore();
    } while (tolower(more) == 'y');
    Info.writeFile();
  }
  else
  {//If the file exist, ask whether to display all the information in the file.
      cout << "  Press 'y' to see the whole list.  "; cin >> yes;
      cin.ignore(); cout << endl;
      if (yes == tolower('y'))
      {   do
          {  cout << "Name stored in DirV is:  " << Info.DirV[index].name <<
                  " phone:  " << Info.DirV[index].phone << "\n\n";
             index++;
          } while (index < Info.DirV.size());
      }
      else { cout << " Bye\n\n"; }
  }
    return 0;
}

I still have not decided whether to complete the program as I think I built the backbone of the program already, the rest are just adding member functions. Still thinking about putting putting the display of names into function and make the main() simpler. This is a very educational experiment, did I had to review the former chapters, read through my notes, improving my notes so I can save a lot of times in the future. The book really doesn't go deep enough into each topic, a lot of things I want to do is not there. My memory ( or the lack of) really slowing me down, there's no other way than to just keep dreaming up programs to make me go back and read through the material over and over.

One think I am still not quite get, why the int variable has to have "static" in front when declaring in the class inside the .h file. that tripped me for a little while.
 
Last edited:
  • #35
Well done, you have reached a big milestone: we can now start talking about how to write object oriented code that is easy to debug, maintain and extend. You see although you have written your code as a class, it doesn't leverage the power of OOP.

One way to start writing a class is to write down what you want it to do.
  • I want a class for a contact directory: a good name will be Contacts.
  • The class will define a struct Contact with the following elements:
    • name
    • phone
  • This class should have public methods that:
    • Read the contacts in from a disk file
    • Write the contacts out to a disk file
    • Create a contact
    • Retrieve a contact
    • Update a contact
    • Delete a contact
    • (these last four operations are commonly known as CRUD)
    • Sort the contacts into alphabetical order
    • List the contacts
You should then be able to create the header file - this will not contain any code, we will leave this to the implementation file. Notice how similar this is to the plain language list above: with practice you will be able to write the first draft of the header file without doing it in plain language first. Note however that I have included some mistakes that you will have to fix :smile:. [Edit- added] Finally, I have kept to your use of C-strings for the name and telephone number (and filename) - you should use std::string for these, it is a lot easier and safer and there is no valid reason not to.

C:
#ifndef Contacts_H
#define Contacts_H
#include <vector>
#include <fstream>

class Contacts {
  private:
    const static int nameL = 25, phoneL = 12;
    // Cache the entries in a vector.
    std::vector <Contact> _entries;

  public:
    struct Contact {
        // Full name.
        char name[nameL];
        // Telephone number.
        char phone[phoneL];
    };

    // Constructor - takes a filename to use for permanent storage.
    void Contacts(char& filename);

    // Read the contacts in from a disk file.
    void read(char& filename);

    // Write the contacts back out to the original file.
    void flush();

    // Create a contact.
    void createContact(Contact& newContact);

    // Retrieve a contact.
    Contact getContact(char& name);

    // Update a contact.
    void updateContact(Contact& updatedContact);

    // Delete a contact.
    void deleteContact(char& name);

    // Sort the contacts into alphabetical order.
    void sort();

    // Get all the contacts so they can be printed out.
    std::vector* getContacts();
};
#endif
 
Last edited:
  • #36
yungman said:
One think I am still not quite get, why the int variable has to have "static" in front when declaring in the class inside the .h file. that tripped me for a little while.
Strictly speaking, it doesn't - is Visual Studio insisting that it should? Note that you have declared nameL and phoneL as const; can you see why there is no point in having a const that is not static?
 
Last edited:
  • #37
pbuk said:
I want a class for a contact directory: a good name will be Contacts.
I agree.
A name for the class that is unhelpful is VectorCall. The name you choose should convey to a third party what kind of object the class is supposed to represent. The class is designed around the vector template class, but that's not relevant to anyone trying to understand what it represents.

There are several other names that are not well-chosen:
  • nameL - better would be nameLen - the most important attribute is that this is the length of some string
  • phoneL - better as phoneLen
  • DirV - why do we care that the underlying container type is a vector?
  • Info - too vague
  • newF - newFile
  • ct2 - ?
  • yes - a terrible name for a variable that could contain 'n'
In the world of software development, several years are likely to go by after the code is written. In the maintenance phase, it's almost certain that a different developer will be tasked to fix bugs that crop up during the life of the software. If the original developer uses names for classes, functions, and variables whose purpose can't be deduced easily, it's much more difficult for a maintenance programmer to understand the code and fix these bugs.

In your constructor you have this code -
C++:
    else
        {//If the file exist, read entire file into vector DirV.
          file.read(reinterpret_cast<char*>(&Dir), sizeof(Dir));
          do
          {
            DirV.push_back(Dir);
            file.read(reinterpret_cast<char*>(&Dir), sizeof(Dir));
          } while (!file.eof());
          file.close();
        }
Why are you calling read() twice?
 
  • Like
Likes yungman
  • #38
Mark44 said:
yes - a terrible name for a variable that could contain 'n'

:eek:

It's a pity that after you wrote those nice long messages about useful naming - they were completely ignored. A real kick in the teeth there. Apart from some future programmer, the real beneficiaries are the people yungman will ask to debug his code the very same day.

In this case, though, you shouldn't worry about it, since "yes" will never contain anything. The cin.ignore() will take care of that. It's a pity that after the long thread on strings, c strings and arrays of char, what yungman took away from all that is "sprinkle some cin.ignore() statements in random places."

Mark44 said:
Why are you calling read() twice?

I think the better question is why is he calling it once? The job of the constructor is to construct the object, not to run all of the code, He shouldn't be reading files in the constructor and he sure shouldn't be writing them in the destructor.
 
Last edited:
  • #39
Vanadium 50 said:
I think the better question is why is he calling it once? The job of the constructor is to construct the object, not to run all of the code, He shouldn't be reading files in the constructor and he sure shouldn't be writing them in the destructor.
The same thought occurred to me, about what the job of a constructor should be.
 
  • #40
The destructor behavior is far, far worse. Suppose you call the constructor, do some running, and then decide that the in-memory copy is not what you want. Maybe it's even corrupted. Now what? If you destroy the bad object, it overwrites the good copy. If you decide to exit without writing, you implicitly call the destructor anyway and still manage to overwrite the good copy
 
  • #41
Mark44 said:
Why are you calling read() twice?
Not being a C++ programmer, I had to spend 30 seconds with Google to learn about the break statement.

Also, not being a C++ programmer, I'd missed the difference between "do {block} while (condition)" and "while (condition) {block};
 
Last edited:
  • #42
Mark44 said:
I agree.
A name for the class that is unhelpful is VectorCall. The name you choose should convey to a third party what kind of object the class is supposed to represent. The class is designed around the vector template class, but that's not relevant to anyone trying to understand what it represents.

There are several other names that are not well-chosen:
  • nameL - better would be nameLen - the most important attribute is that this is the length of some string
  • phoneL - better as phoneLen
  • DirV - why do we care that the underlying container type is a vector?
  • Info - too vague
  • newF - newFile
  • ct2 - ?
  • yes - a terrible name for a variable that could contain 'n'
In the world of software development, several years are likely to go by after the code is written. In the maintenance phase, it's almost certain that a different developer will be tasked to fix bugs that crop up during the life of the software. If the original developer uses names for classes, functions, and variables whose purpose can't be deduced easily, it's much more difficult for a maintenance programmer to understand the code and fix these bugs.

In your constructor you have this code -
C++:
    else
        {//If the file exist, read entire file into vector DirV.
          file.read(reinterpret_cast<char*>(&Dir), sizeof(Dir));
          do
          {
            DirV.push_back(Dir);
            file.read(reinterpret_cast<char*>(&Dir), sizeof(Dir));
          } while (!file.eof());
          file.close();
        }
Why are you calling read() twice?
Thanks for the suggestion. The reason I wrote the read like this is because I ran into problem reading the last element twice before. I did talk about this here and I wrote down in my notes to do this. Maybe this doesn't matter, but I don't want to take the chance and just put one more line.

Thanks
 
  • #43
yungman said:
Thanks for the suggestion. The reason I wrote the read like this is because I ran into problem reading the last element twice before.
It's not just bad form. It is actually wrong as written. What if you are reading from an empty file?
 
  • #44
Thanks everyone for the feedback. I did pay attention and understand about the importance of the names. It's not that I ignore all the suggestions from before. My situation is different, I am NOT a student, there is absolutely no chance I'll be working with anyone on programming in this life except if my grandkids ever want to work with grandpa. If that time comes(ever comes), you bet I will remember what you guys said. There is not going to be anyone else working with more programs, so I decided to shortcut it. The only one that needs to understand is me alone, I have my way of simplifying the names and I don't have issue with that.

Regarding to Constructor and Distructor. As I said before whether I am right or wrong, I do not want to run the HD or flash heavily not only for the slow speed, more importantly, both have finite number of cycles. I don't want to wear them out to do the heavy adding, deleting, sorting on the file. My goal started with the definition that I download the content to a vector, the rest of the program will work on the vector alone. Then upon ending the program, I write the updated file back to the HD. That is read once and write once to the HD or flash. Where better to put this task in Constructor and Distructor.

I totally disagree with the notion to worry about data corruption that might destroy the original good data in the file like Vanadium 50 said. So if you work with the file directly, every time you add or delete, you have to shift the elements up or down, when sorting, you read and write so many times. Your chance of screwing up the file is much higher than do it one time. Besides, hopefully user will detect the corruption of data and not save it. Screw up the file totally or partially is still screwing the file up, what's the difference? Data is destroyed either way. I might add the option in the program NOT to save to file if things looks bad though.

Again, thanks for all your help in getting this far. This is the first time I wrote this and this program is not done. I am thinking about using switch-case to select add, delete, display, display range into the program. This is just the bare bone of the program showing the data path and how I view in moving data. This part to me is the most important and most difficult. The other more fancy stuffs like options and sorting is much easier that this to me.

Thanks All.
 
Last edited:
  • #45
If I were writing this program, I would consider using two classes: one for an individual directory entry, and one for the directory as a whole.

The first class would contain name, phone number etc. for a single directory entry. It woud have member functions to create a new entry, read a single entry from disk, write it to disk, display the data, and edit the data (all applying to a single directory entry).

The second class would contain a vector whose elements are directory entries (i.e. objects of the first class). It would have member functions to read the entire directory from disk, write it to disk, search for a given name, create a new entry and insert it into the directory, display the entire directory in a table format, sort the directory by name or phone number, etc. These would all call upon member functions from the first class as necessary.
 
  • Like
Likes yungman
  • #46
yungman said:
My situation is different, I am NOT a student, there is absolutely no chance I'll be working with anyone on programming in this life except if my grandkids ever want to work with grandpa.
Wrong. You ARE the student, and we who respond to your questions are in the role of teacher.
yungman said:
If that time comes(ever comes), you bet I will remember what you guys said. There is not going to be anyone else working with more programs, so I decided to shortcut it. The only one that needs to understand is me alone, I have my way of simplifying the names and I don't have issue with that.
The time to remember what we've said is NOW, not some far-off time. You post your programs, and we attempt to make sense of them, despite your use of unhelpful and meaningless names.
You are NOT the only one who needs to understand your programs. How much harder do you think it is for us to help you if you seemingly go out of your way to make things more difficult to understand?
Maybe you're happy with your way of "simplifying the names," but from this side, all they do is convince us of your confusion.
yungman said:
Regarding to Constructor and Distructor. As I said before whether I am right or wrong, I do not want to run the HD or flash heavily not only for the slow speed, more importantly, both have finite number of cycles.
Using the hard drive or flash drive is totally irrelevant to what the constructor and destructor should be doing, which is creating an object and then destroying it, respectively. There should be other member functions that are tasked with filling an object with data, adding a contact, deleting a contact, etc. None of this should be done in a constructor.

In any case, I found a site that talks about the mean time before failure (MTBF) for typical hard drives -- somewhere between 100,000 and 1,000,000 hours, or between 11 and 110 years. Do you seriously think that your program is going to exercise a hard drive that much? Your concern is very much misplaced.
yungman said:
I totally disagree with the notion to worry about data corruption that might destroy the original good data in the file like Vanadium 50 said. So if you work with the file directly, every time you add or delete, you have to shift the elements up or down, when sorting, you read and write so many times. Your chance of screwing up the file is much higher than do it one time. Besides, hopefully user will detect the corruption of data and not save it. I might add the option in the program NOT to save to file if things looks bad though.
IMO you are NOT in a position to disagree with what @Vanadium 50 said, as you have nowhere near the knowledge of programming that he has. In his comment he gave a reason for his opinion - ignore it at your peril.
 
Last edited:
  • Like
Likes Vanadium 50
  • #47
jbriggs444 said:
Not being a C++ programmer, I had to spend 30 seconds with Google to learn about the break statement.

It's sibling, continue, is even more amazing.
 
  • #48
jtbell said:
If I were writing this program, I would consider using two classes: one for an individual directory entry, and one for the directory as a whole.

The first class would contain name, phone number etc. for a single directory entry. It woud have member functions to create a new entry, read a single entry from disk, write it to disk, display the data, and edit the data (all applying to a single directory entry).

The second class would contain a vector whose elements are directory entries (i.e. objects of the first class). It would have member functions to read the entire directory from disk, write it to disk, search for a given name, create a new entry and insert it into the directory, display the entire directory in a table format, sort the directory by name or phone number, etc. These would all call upon member functions from the first class as necessary.
Thanks, I have to think more, this is the starting point of the program, I am still thinking about the other add, delete, sort options. I will likely have more than one class. It's a challenge so far to put this together.

Thanks
 
  • #49
Mark44 said:
Wrong. You ARE the student, and we who respond to your questions are in the role of teacher.
The time to remember what we've said is NOW, not some far-off time. You post your programs, and we attempt to make sense of them, despite your use of unhelpful and meaningless names.
You are NOT the only one who needs to understand your programs. How much harder do you think it is for us to help you if you seemingly go out of your way to make things more difficult to understand?
Maybe you're happy with your way of "simplifying the names," but from this side, all they do is convince us of your confusion.
Using the hard drive or flash drive is totally irrelevant to what the constructor and destructor should be doing, which is creating an object and then destroying it, respectively. There should be other member functions that are tasked with filling an object with data, adding a contact, deleting a contact, etc. None of this should be done in a constructor.

In any case, I found a site that talks about the mean time before failure (MTBF) for typical hard drives -- somewhere between 100,000 and 1,000,000 hours, or between 11 and 110 years. Do you seriously think that your program is going to exercise a hard drive that much? Your concern is very much misplaced.
IMO you are NOT in a position to disagree with what @Vanadium 50 said, as you have nowhere near the knowledge of programming that he has. In his comment he gave a reason for his opinion - ignore it at your peril.
To clarify, I meant I am not a college student that have the whole career in front of me. Yes, I am a student here learning C++.

Point taken on the names.

Thanks
 
  • #50
jbriggs444 said:
It's not just bad form. It is actually wrong as written. What if you are reading from an empty file?
I have to experiment more with that, I did run into problem before. What will happen reading an empty file? Is it just reading garbage or can it do damage to the file?

Thanks
 
  • #51
yungman said:
To clarify, I meant I am not a college student that have the whole career in front of me.
I'm fairly sure that was already clear to most of us.
 
  • #52
About the part that read the file two times. This is the problem I am trying to avoid. Here is what happened when I deleted the read before the do-while loop:I made the first file.read into comment line. I move the DirV.push_back below the file read in the loop.

One read file.jpg


This is the result, the last line is repeated.
Read error.jpg


I have to think of some other way to fix this if I have to eliminate one read. BUT still, that doesn't help reading an empty file. The first read will read empty file.

What are the problems reading an empty file? If just reading garbage, then it's not too bad. But if it can do other damage, then it's not good.

Thanks
 

Attachments

  • One read file.jpg
    One read file.jpg
    34.4 KB · Views: 179
  • #53
yungman said:
I have to experiment more with that, I did run into problem before. What will happen reading an empty file? Is it just reading garbage or can it do damage to the file?
Step through the code mentally, thinking through what each line will do. Let us pull that code section down here so that we can examine it more easily.
C++:
    else
        {//If the file exist, read entire file into vector DirV.
          file.read(reinterpret_cast<char*>(&Dir), sizeof(Dir));
          do
          {
            DirV.push_back(Dir);
            file.read(reinterpret_cast<char*>(&Dir), sizeof(Dir));
          } while (!file.eof());
          file.close();
        }

Line 3: we start by attempting to read from the file into an array element. But the file is empty. So the read fails and the end file.eof() flag is set.

Line 4-5: We enter a do while loop. Since this is a do-while, there is no initial test of the termination condition.

Line 6: We do a DirV.push_back(Dir). One supposes that this somehow commits an array item to the in-memory directory. Based on the name "push_back", this is likely a metaphorical stack push. But the array entry we are pushing is garbage -- the read had failed.

Line 7: We attempt to read a second record, oblivious to the fact that we've already hit end of file.

Line 8: We check the loop termination condition. [One hopes and expects that the EOF condition remains set after multiple attempts to read past end of file]

Line 9: We close the file.
 
Last edited:
  • #54
yungman said:
About the part that read the file two times. This is the problem I am trying to avoid. Here is what happened when I deleted the read before the do-while loop:
Rule: Think before you code. You want to figure out the failure modes when writing the code, not when running it.

Rule: Always check the status code. [This one I picked up in a course on VMS system programming in the 80s. It was advice well worth listening to].

You want the sequence of operations to be:

Read
Test for end of file (not yet)
Push
Read next
Test for end of file (not yet)
Push
...
Read next
Test for end of file (EOF - exit)

There are a lot of ways to put that into a while loop.

You could put one read outside the loop and use:
Code:
Read
while not EOF
    Push
    Read
end while

Thinking back to first year programming in the '70's, when structured programming was just becoming all the rage, this is pretty much what we were taught to do.

Or you could test for the exit condition mid-loop

Code:
while (true)
    read
    if EOF then break
    push
end while

This has the advantage that you're not coding the read statement twice. And it puts the exit test where it belongs -- right when the exit condition arises.

I often find myself using the following construct - emulating a break.

Code:
DONE = false
while ( not DONE )
    read
    if not EOF
    then
        push
    else
        DONE = true
    end if
end while
 
Last edited:
  • #55
yungman said:
Where better to put this task in Constructor and Distructor.

In methods on the object with names like read_data and write_data.

You do not want to put code that does I/O into a constructor or destructor. The constructor and destructor should only be for allocating and deallocating memory for the object itself (which is done automatically by the compiler), not for reading data from disk or writing data to disk. If you try to read data from disk in an object's constructor, and something goes wrong, the object will be in an inconsistent state. If you try to write data to disk in an object's destructor, and something goes wrong, the object is gone and you have no way to recover from the error.
 
  • Like
Likes pbuk, Vanadium 50 and yungman
  • #56
PeterDonis said:
In methods on the object with names like read_data and write_data.

You do not want to put code that does I/O into a constructor or destructor. The constructor and destructor should only be for allocating and deallocating memory for the object itself (which is done automatically by the compiler), not for reading data from disk or writing data to disk. If you try to read data from disk in an object's constructor, and something goes wrong, the object will be in an inconsistent state. If you try to write data to disk in an object's destructor, and something goes wrong, the object is gone and you have no way to recover from the error.
Ah, I see what you mean about writing in Distructor, that if that fails, the program is closed. So I should use member function to write to file.

But what's wrong loading the file with Constructor? What do you mean the object will be in an inconsistent state?

Thanks
 
  • #57
jbriggs444 said:
This has the advantage that you're not coding the read statement twice.

The "C/C++ way" to do that is do...while. That executes at least once.
 
  • #58
yungman said:
what's wrong loading the file with Constructor? What do you mean the object will be in an inconsistent state?

If an error happens inside the constructor, there is no guarantee that the object will be constructed correctly. So you should not do anything inside a constructor that might cause an error.
 
  • #59
Vanadium 50 said:
The "C/C++ way" to do that is do...while. That executes at least once.
Possibly I am missing something. That would seem to mean that the exit condition needs to be checked twice.
Code:
do {
    read
    if not EOF
        push
    end if
} while not EOF
 
  • #60
jbriggs444 said:
Rule: Think before you code. You want to figure out the failure modes when writing the code, not when running it.

Rule: Always check the status code. [This one I picked up in a course on VMS system programming in the 80s. It was advice well worth listening to].

You want the sequence of operations to be:

Read
Test for end of file (not yet)
Push
Read next
Test for end of file (not yet)
Push
...
Read next
Test for end of file (EOF - exit)

There are a lot of ways to put that into a while loop.

You could put one read outside the loop and use:
Code:
Read
while not EOF
    Push
    Read
end while

Thinking back to first year programming in the '70's, when structured programming was just becoming all the rage, this is pretty much what we were taught to do.

Or you could test for the exit condition mid-loop

Code:
while (true)
    read
    if EOF then break
    push
end while

This has the advantage that you're not coding the read statement twice. And it puts the exit test where it belongs -- right when the exit condition arises.

I often find myself using the following construct - emulating a break.

Code:
DONE = false
while ( not DONE )
    read
    if not EOF
    then
        push
    else
        DONE = true
    end if
end while
I tried your suggestion:

One read file.jpg


It still print out the last line twice.

I wish I can find the thread that we talked about this, it's tricky. The eof() does not become true right away and double write the last data.
 

Similar threads

Replies
10
Views
2K
  • · Replies 30 ·
2
Replies
30
Views
4K
  • · Replies 52 ·
2
Replies
52
Views
4K
  • · Replies 75 ·
3
Replies
75
Views
6K
Replies
5
Views
2K
  • · Replies 36 ·
2
Replies
36
Views
4K
Replies
2
Views
2K
  • · Replies 2 ·
Replies
2
Views
1K
  • · Replies 6 ·
Replies
6
Views
2K
  • · Replies 89 ·
3
Replies
89
Views
6K