Thread: Need help

  1. #1
    Registered User
    Join Date
    Apr 2007
    Posts
    10

    Need help

    OK, I'm trying to make this thing work but it won't write on file. If I put only data about one student with "invisible" 4 that I added explicitly it will print him, but if I add more than one then it breaks... literally, not "break;". And it seems that it's not putting it in file .dat.

    Class applications works fine, but I putted it in just in case. Hope that there won't be any error since I spend few mins trying to translate from my language to english.

    Don't know where is error, might be in "this", or I might have put wrong "ios::"

    I'd appreciate help since I lost 3 additional hours trying to figure why it's not working.

    And compiling and building it don't pop up any error.

    Code:
    #include <iostream>
    #include <fstream>
    
    void input (char* chr)
    {
    	std::cin.getline (chr 50);
    	if (std::cin.gcount() == 1)
    		std::cin.getline (chr, 50);
    };
    
    std::fstream dat;
    
    class cstudents
    {
    public:
    	int nmbr, year_sign;
    	char lname_name[50];
    	void input_student();
    	void print_student(int);
    };
    
    void cstudents::input_student()
    {
    	char more;
    	do
    	{
    		dat.open("Students.dat", std::ios::out | std::ios::binary);
    		std::cout<<"Input number of student: "; std::cin>>this->nmbr;
    		std::cout<<"Input last name and name of student: "; input(this->lname_name);
    		std::cout<<"Input year of sign of student: "; std::cin>>this->year_sign;
    
    		dat.write ((char*)this, sizeof(this));
    
    		std::cout<<"More students (y\\n)?"; std::cin>>more;
    	} while (jos == 'y' || jos == 'Y');
    	dat.close();
    };
    
    void cstudents::print_student (int nmbr){
    	dat.open ("Students.dat", std::ios::in | std::ios::binary);
    	while (1)
    	{
    		dat.read((char*)this, sizeof(this));
    		if (dat.eof())
    			break;
    		if (this->nmbr == nmbr)
    		{
    			std::cout<<"Number of student "<<this->nmbr<<std::endl;
    			std::cout<<"Last and first name: "<<this->lname_name<<std::endl;
    			std::cout<<"Year of sign: "<<this->year_sign<<std::endl;
    		};
    	};
    	dat.close();
    };
    //-----------------------------------------------------------------------------------------------------------
    //this works, or it did worked before translation, so it still works with original version.
    //-----------------------------------------------------------------------------------------------------------
    class capplications
    {
    private:
    	capplications* next;
    public:
    	int pass_app, nmbr, grade;
    	char name_class[50], date [12];
    	void input_application();
    	float average_grade (int);
    	capplications ();
    };
    
    capplications::capplications ()
    {
    	next = NULL;
    };
    
    void capplications::input_application()
    {
    	capplication *last = this, *another;
    	while (last->next)
    		last = last -> next;
    	another = new capplications;
    	last->next = another;
    	std::cout<<"Input number of application: "; std::cin>>another->pass_app;
    	std::cout<<"Input number of student: "; std::cin>>another->nmbr;
    	std::cout<<"Input name of class: "; input (another->name_class);
    	std::cout<<"Input when is exam: "; input (another->date);
    	std::cout<<"Input grade for exam: "; std::cin>>another->grade;
    };
    
    float capplications::average_grade (int nmbr)
    {
    	int sum = 0, amount = 0;
    	capplications* current = this->next;
    	while (current)
    	{
    		if (current->nmbr == nmbr)
    		{
    			amount += current->grade;
    			++sum;
    		};
    		current = current->next;
    	};
    	return (amount/sum);
    };
    //-----------------------------------------------------------------------------------------------------------
    //main
    //-----------------------------------------------------------------------------------------------------------
    int main()
    {
    	int choice, nmbr;
    	capplications *application = new capplications;
    	cstudents student;
    	do
    	{
    		std::cout<<"1. Input data about students in file"<<std::endl;
    		std::cout<<"2. Input data about application"<<std::endl;
    		std::cout<<"3. Average grade of student"<<std::endl;
    		std::cout<<"9. Exit"<<std::endl;
    		std::cin>>choice;
    		switch (choice) {
    		case 1: students.input_student(); break;
    		case 2: applications->input_application(); break;
    		case 3:
    			std::cout<<"Number of student: "; std::cin>>nmbr;
    			students.print_student(nmbr);
    			std::cout<<"Average grade: " << prijave->prosjek_ocjena(mat_br) <<std::endl;
    			break;
    		case 4:std::cin>>nmbr; students.print_student(nmbr); break;
    		};
    	} while (choice != 9);
    	return 0;
    }

  2. #2
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Code:
    	capplication *last = this, *another;
    	while (last->next)
    		last = last -> next;
    This assumes that last is not NULL. If that's a valid assumption then you might consider mentioning it somewhere.

    Code:
    void capplications::input_application()
    {
    	capplication *last = this, *another;
    	while (last->next)
    		last = last -> next;
    	another = new capplications;
    	last->next = another;
    	std::cout<<"Input number of application: "; std::cin>>another->pass_app;
    	std::cout<<"Input number of student: "; std::cin>>another->nmbr;
    	std::cout<<"Input name of class: "; input (another->name_class);
    	std::cout<<"Input when is exam: "; input (another->date);
    	std::cout<<"Input grade for exam: "; std::cin>>another->grade;
    };
    You don't set another->next. This could cause some problems (like if this function is called again).

    You never free any of that memory.

    Code:
    void input (char* chr)
    {
    	std::cin.getline (chr 50);
    	if (std::cin.gcount() == 1)
    		std::cin.getline (chr, 50);
    };
    I'm assuming that that missing ',' is just a typo . . . and why not have a while loop, in case it fails the second time too?

    Code:
    return (amount/sum);
    amount and sum are integers, and yet the function returns a float. That float will be truncuated. I suggest you cast one of the numbers to a float (or make it a float in the first place).

    csutdents::print_student() opens and closes dat perfectly well; but cstudents::input_student() opens dat multiple times (or it could) but only closes it once.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  3. #3
    Registered User
    Join Date
    Apr 2007
    Posts
    10
    Thanks, I'll try it with that multiple open. Haven't noticed it.

    And about
    Code:
    another->next
    is defined in constructor:

    Code:
    capplications::capplications ()
    {
    	next = NULL;
    };
    And one more question, the thing that bothered me most is usage of
    Code:
    this
    in class cstudent. Is it right? I used only
    Code:
    this
    in lists so far, so it was kinda compromise.
    Last edited by ranko_6; 04-11-2007 at 04:30 PM.

  4. #4
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    D'oh. Of course. And of course last won't be NULL unless this is. That's what happens when you have only a few minutes of computer time left . . .

    BTW, some people (myself included) don't like naming variables pluraly, like capplications, cstudents, etc. I like singular.

    Code:
    	while (current)
    	{
    		if (current->nmbr == nmbr)
    		{
    			amount += current->grade;
    			++sum;
    		};
    		current = current->next;
    	};
    Semicolons are not required after ifs and loops.

    Code:
    void cstudents::input_student()
    {
    	char more;
    	do
    	{
    		dat.open("Students.dat", std::ios::out | std::ios::binary);
    		std::cout<<"Input number of student: "; std::cin>>this->nmbr;
    		std::cout<<"Input last name and name of student: "; input(this->lname_name);
    		std::cout<<"Input year of sign of student: "; std::cin>>this->year_sign;
    
    		dat.write ((char*)this, sizeof(this));
    
    		std::cout<<"More students (y\\n)?"; std::cin>>more;
    	} while (jos == 'y' || jos == 'Y');
    	dat.close();
    };
    You don't use jos anywhere else that I can see. I'm guessing you meant more.

    Also, tolower() or toupper() could make your job easier.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  5. #5
    Registered User
    Join Date
    Apr 2007
    Posts
    10
    Sorry haven't noticed it, "jos" is in my language for "more".
    About semicolons after loops and if's, I got used to it and it's kinda same, except more typing from my side.

    I putted dat.open so it won't open students multiple times. Now i have trouble that when I read from file I can read from it only once. Like if I put

    Input number of student: 1
    Input last name and name of student: Ranko P
    Input year of sign of student: 1234

    Select 4 for output I get:

    Number of student: 1
    Last and first name: Ranko P
    Year of sign: 1234

    but if I then press 4 (for output) again and input number 1 (number of student) I don't get anything. It's like the file is empty. Or if I input more than one, if I list one of them, second time I don't get anything, like file is empty.

    Don't know about the case but as far as I know "ios::'s" I use here don't empty file after reading it unless it's
    Code:
    ios::out \\without "ios::ate"
    I've run in this kind of problem with one example too so it might be problem from my side.

    Code:
    #include <iostream>
    #include <fstream>
    
    void input (char* chr)
    {
    	std::cin.getline (chr 50);
    	if (std::cin.gcount() == 1)
    		std::cin.getline (chr, 50);
    };
    
    std::fstream dat;
    
    class cstudents
    {
    public:
    	int nmbr, year_sign;
    	char lname_name[50];
    	void input_student();
    	void print_student(int);
    };
    
    void cstudents::input_student()
    {
    	char more;
    	dat.open("Students.dat", std::ios::out | std::ios::binary); // moved this here
    	do
    	{
    		//dat.open("Students.dat", std::ios::out | std::ios::binary); from here
    		std::cout<<"Input number of student: "; std::cin>>this->nmbr;
    		std::cout<<"Input last name and name of student: "; input(this->lname_name);
    		std::cout<<"Input year of sign of student: "; std::cin>>this->year_sign;
    
    		dat.write ((char*)this, sizeof(cstudents)); //change from sizeof(this) to sizeof(cstudents)
    
    		std::cout<<"More students (y\\n)?"; std::cin>>more;
    	} while (more == 'y' || more == 'Y');
    	dat.close();
    };
    
    void cstudents::print_student (int nmbr){
    	dat.open ("Students.dat", std::ios::in | std::ios::binary);
    	while (1)
    	{
    		dat.read((char*)this, sizeof(cstudents)); //here too... sizeof(cstudents)
    		if (dat.eof())
    			break;
    		if (this->nmbr == nmbr)
    		{
    			std::cout<<"Number of student "<<this->nmbr<<std::endl;
    			std::cout<<"Last and first name: "<<this->lname_name<<std::endl;
    			std::cout<<"Year of sign: "<<this->year_sign<<std::endl;
    		};
    	};
    	dat.close();
    };
    Last edited by ranko_6; 04-11-2007 at 05:08 PM.

  6. #6
    Registered User
    Join Date
    Apr 2007
    Posts
    10
    Made one more test. It writes in .dat perfectly, so if I close it and reopen it, then choose to output student without inputing them, I can read again only one normally.
    Then I need to exit, rerun it and then I can list another one.

    Could I make it work with tellg and seekg?

  7. #7
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Could I make it work with tellg and seekg?
    Probably, but you have to remember to flush the file when switching between reading and writing.

    Code:
    dat.write ((char*)this, sizeof(cstudents)); //change from sizeof(this) to sizeof(cstudents)
    You could also use sizeof(*this).

    Code:
    void input (char* chr)
    {
    	std::cin.getline (chr 50);
    	if (std::cin.gcount() == 1)
    		std::cin.getline (chr, 50);
    };
    Do you still have that in? You don't need a semicolon; you do need a comma; and a do-while loop would work better.
    Code:
    void input (char* chr)
    {
    	do {
    		std::cin.getline (chr, 50);
    	} while (std::cin.gcount() == 1);
    };
    Also, writing a class to a file directly is a bad idea. If it contains any pointers or references those will become invalid when you load it (in all likelyhood). It's best to have a class::write() function that writes the essential data to the file. (Even writing ints can be non-portable, if you switch to a system with different edianness.)
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  8. #8
    The larch
    Join Date
    May 2006
    Posts
    3,573
    And one more question, the thing that bothered me most is usage of this in class cstudent. Is it right? I used only this in lists so far, so it was kinda compromise.
    It is correct but unneeded: if you use a class variable name in a member function, this is meant implicitly. There are other conventions, if you feel you need to distinguish class variables, like prefixing m_ (m_classvariable) or appending an underscore (classvariable_).

    I see in your code, however, that you are passing arguments to a member function with the same name as a class variable. In this case this-> is required, because the local function argument otherwise shadows the class variable.
    However, I don't think that creating such naming "conflicts" (although legal) is too good of an idea. You might still get confused which one you meant. In this respect using some naming convention for class members could be better (because it is easy to run out of good variable-names otherwise).

  9. #9
    Registered User
    Join Date
    Apr 2007
    Posts
    10
    dwks thanks for info on do - while.
    I see in your code, however, that you are passing arguments to a member function with the same name as a class variable.
    I got used to this kind of confusion, but will try to improve myself in this area and make future code little bit clearer.

    Thank you all for help. Inability to read file twice in a row... still not sure hot to handle it but I "quited" from solving that problem since, even "working" example from book couldn't read more than once, so even if I'm not sure, I think it may have connection with that that I'm compiling it on Virtual Machine.

  10. #10
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    I did not read this thread entirely, but if you are reusing an fstream variable and having trouble the second time you use it, make sure you call clear() after you close() it each time to clear any error states.

  11. #11
    Registered User
    Join Date
    Apr 2007
    Posts
    10
    Thanks Daved, worked like a charm.

  12. #12
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    You're welcome. I think a better solution might be to make the fstream variable local to the functions that use it rather than global. Then when it goes out of scope it will close itself, and the next time the function is called a different fstream will be used that doesn't need to be cleared.

Popular pages Recent additions subscribe to a feed