CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 12 of 12
  1. #1
    Join Date
    Mar 2005
    Posts
    40

    comparing objects from a binary file

    I have created a binary file with 30 locations (from 0 -> 29). It's supposed to store objects of this class:
    Code:
    class employee {
    private:
    	char name[25],telephone[15];
    	int room;
    public:
    	employee(char []="",char []="",int=0);
    	char *getname() { return name; }
    	char *gettelephone() { return telephone; }
    	int getroom() { return room; }
    	employee &setname(char n[]) { 
    		strcpy(name,n);
    		return *this;
    	}
    	employee &settelephone(char t[]) {
    		strcpy(telephone,t);
    		return *this;
    	}
    	employee &setroom(int r) {
    		room=r;
    		return *this;
    	}
    };
    
    employee::employee(char n[],char t[],int r) {
    	strcpy(name,n);
    	strcpy(telephone,t);
    	room=r;
    }
    Now, inside main I have this function being called:
    Code:
    void addEmployee(employee &em,fstream &df) {
    	employee em2;
    	int soem=sizeof(em);
    	char name[25],telephone[15];
    	int room;
    	cout<<"Enter the name of the employee.\n";
    	cin>>name;
    	cout<<"Enter the room of the employee.\n";
    	cin>>room;
    	cout<<"Enter the telephone number of the employee.\n";
    	cin>>telephone;
    	em.setname(name).setroom(room).settelephone(telephone);
    	bool wrote=false;
    	if (findEmployee(em,df) == -1) {
    		for (int i=0;i<30;i++) {
    			df.seekg(i*soem);
    			df.read(reinterpret_cast<char *>(&em2),soem);
    			if (em2.getroom()==0) {
    				wrote=true;
    				df.seekp(i*soem);
    				df.write(reinterpret_cast<const char *>(&em),soem);
    				break;
    			}
    		}
    		if (wrote)
    			cout<<"Succesfully added employee to database.\n\n";
    		else cout<<"Database is full.\n\n";
    	} else
    		cout<<"An Employee with those information already exists.\n\n";
    }
    and this is the findEmployee function:
    Code:
    int findEmployee(employee &em,fstream &df) {
    	int index=-1;
    	employee em2;
    	for (int i=0;i<30;i++) {
    		df.seekg(i*sizeof(em2));
    		df.read(reinterpret_cast<char *>(&em2),sizeof(em2));
    		if (strcmp(em2.getname(),em.getname()) == 0 && 
    strcmp(em2.gettelephone(),em.gettelephone()) == 0
    			&& em.getroom() == em2.getroom()) {
    			index=i;
    			break;
    		}
    	}
    	return index;
    }
    It seems to always say that the employee does not exist, even though I am entering the same data everytime. How can I compare objects froma binary file?
    Last edited by zifeer; January 20th, 2006 at 01:13 PM. Reason: code fixes

  2. #2
    Join Date
    Feb 2000
    Location
    San Diego, CA
    Posts
    10,354

    Re: comparing objects from a binary file

    How are you serializing the employee info into the file ?
    You need to match the writes to the reads in exactly the same way.
    Code:
    df.read(reinterpret_cast<char *>(&em2),sizeof(em2));
    This one looks suspicious to me..

  3. #3
    Join Date
    Apr 1999
    Posts
    27,449

    Re: comparing objects from a binary file

    This is totally incorrect. The reading and writing you're doing must be thrown away. Employee is a non-POD object, therefore it cannot be written to a file this way. It must be properly serialized, not just written as one blob.

    This has been discussed many times (and this is not really a Visual C++ issue -- this should be moved to the non-Visual C++ forum).

    Here are links to the previous discussions of properly serializing non-POD classes.

    http://www.codeguru.com/forum/showth...=serialization

    Also, it isn't wise just to take any struct/class and write it as binary this way. What happens if you save a file with one version of the compiler, and then you upgrade compilers and recompile your code? The size of the struct or class may be different with the new compiler, making your new program read the old files incorrectly.

    If you want to write a binary file, the layout of the binary file should be under your control, and not under the control of a particular struct alignment or object layout that the compiler happens to use.

    But the bottom line is that you can't write non-POD objects in the way you're doing it now.

    Regards,

    Paul McKenzie
    Last edited by Paul McKenzie; January 20th, 2006 at 01:33 PM.

  4. #4
    Join Date
    Mar 2005
    Posts
    40

    Re: comparing objects from a binary file

    So the idea is to use >> and << instead of .write() and .read() functions? Will that still be valid for reading from every entry, I mean I can still use .seekp() and .seekg() to get into the entries I want?

  5. #5
    Join Date
    Apr 1999
    Posts
    27,449

    Re: comparing objects from a binary file

    You can still use write(), read(), etc. The issue is what you are writing and reading.

    You can't write the object as a blob to the file. You must write the different data to the file in a manner where you can read it back correctly. See the link I posted in my earlier message.

    Regards,

    Paul McKenzie

  6. #6
    Join Date
    Mar 2005
    Posts
    40

    Re: comparing objects from a binary file

    I tried testing whether the objects are being read correctly, so I created a binary file which accepts employee entries and wrote 3 entries into it:
    Code:
    #include <stdlib.h>
    #include <iostream.h>
    #include <fstream.h>
    #include <cstring>
    
    class Employee {
    public:
    	Employee(int=0,char []="",char []="",float=0.0);
    	int getID() { return id; }
    	char *getFname() { return fname; }
    	char *getLname() { return lname; }
    	float getGrade() { return grade; }
    	Employee &setID(int i) {
    		id=i;
    		return *this;
    	}
    	Employee &setFname(char *f) {
    		strcpy(fname,f);
    		return *this;
    	}
    	Employee &setLname(char *l) {
    		strcpy(lname,l);
    		return *this;
    	}
    	Employee &setGrade(float g) {
    		grade=g;
    		return *this;
    	}
    private:
    	int id;
    	char fname[20],lname[20];
    	float grade;
    };
    
    Employee::Employee(int i,char *f,char *l,float g) {
    	id=i;
    	strcpy(fname,f);
    	strcpy(lname,l);
    	grade=g;
    }
    
    int main() {
    	ofstream outfile("Employee.dat",ios::binary);
    	if (!outfile) {
    		cerr<<"Could not open file.\n";
    		exit(1);
    	}
    	Employee e(5,"bla","bli",78);
    	Employee e2(1,"foo","bar",93);
    	Employee e3(12,"cow","noo",55);
    	outfile.seekp((e.getID()-1)*sizeof(e));
    	outfile.write(reinterpret_cast<const char *>(&e),sizeof(e));
    	outfile.seekp((e2.getID()-1)*sizeof(e2));
    	outfile.write(reinterpret_cast<const char *>(&e2),sizeof(e2));
    	outfile.seekp((e3.getID()-1)*sizeof(e3));
    	outfile.write(reinterpret_cast<const char *>(&e),sizeof(e3));
    	return 0;
    }
    Then, tried to read all entries and show the output, like this:
    Code:
    int main() {
    	ifstream infile("Employee.dat",ios::binary);
    	if (!infile) {
    		cerr<<"Could not open file.\n";
    		exit(1);
    	}
    	Employee e;
    	for (int i=0;i<16;i++) {
    		infile.seekg(i*sizeof(e));
    		infile.read(reinterpret_cast<char *>(&e),sizeof(e));
    		if (e.getID() != 0)
    			cout<<e.getFname()<<" "<<e.getLname()<<" "<<e.getID()<<" "<<e.getGrade()<<endl;
    		else cout<<i<<" empty\n";
    	}
    	return 0;
    }
    and the output was:
    bla bli 1 93
    1 empty
    2 empty
    3 empty
    foo bar 5 78
    5 empty
    6 empty
    7 empty
    8 empty
    9 empty
    10 empty
    cow moo 12 55
    cow moo 12 55
    cow moo 12 55
    cow moo 12 55
    cow moo 12 55
    So I think the data is being read and wrote correctly, but the problem here seems to be that when I wrote to the 11th entry, all entries from 11 to 15 contained the same object. Why's that?

  7. #7
    Join Date
    Apr 1999
    Posts
    27,449

    Re: comparing objects from a binary file

    I guess you didn't care for what I stated, either in this thread or the other one I gave you. Again, what you are doing is wrong. Your current code is not legitimate, regardless of how it may fool you into thinking it is. So you can't get an answer to your current problem until you change your code to do the serialization properly.

    First, the code you wrote is not guaranteed to work. You cannot read and write into a non-POD object this way. Regardless of what results you got, it is undefined behavior to initialize a non-POD object by writing a set of bytes directly into it. The undefined behavior could be from everything "works", to crashing every time.

    You initialize a non-POD object by calling the object's public member functions. For example, Employee::SetName() would set the name, Employee::SetGrade() sets the grade, etc. Similarly a WriteName() would write the name to a file, etc.

    Since your object contains a user-defined constructor, it is no longer a POD type, therefore it must be treated as non-POD. Included in this treatment of non-POD is the correct setting of the members, and that can only be achieved by calling public member functions to set the members correctly, or if your members are public (which is a bad idea anyway), then you must set these member individually.

    The solution to your problem can be easily achieved by two ways:

    1) Change your writing to write the different pieces of data individually, same for reading the data (write the name, write the grade -- read name, read grade). This is the correct way to serialize an object.

    or

    2) Factor out the POD data from Employee into a separate struct or class, and write that POD data instead of the Employee class:
    Code:
    struct EmployeeInformation
    {
        char firstname[20];
        char lastname[20];
        float grade;
        int id;
    };
    
    class Employee
    {
         private:
              EmployeeInformation Info;
    
         public:
              SetInfo(const EmployeeInformation& theInfo)
              {
                  Info = theInfo;
              }
    
               EmployeeInformation GetInfo() const { return Info; }
    //...
    };
    Then for each employee, you call GetInfo() and write the information as a blob, because EmployeeInformation is now a POD type. Similarly for reading, your main() program creates an EmployeeInformation type, reads into it, and then calls SetInfo() for the Employee.

    You have two legitimate choices as to how to save the data. Your code also uses char arrays for the data. Your code makes no check to see if the name is greater than 19 characters. The way to work around this is to make those char arrays std::string, but then you must use method 1 I mentioned above, or use method 2, but then you must write the data from the string object individually. Therefore the first method is the best, while the second will make your program work properly for now (i.e. but still has flaws).

    Also, if you expand your Employee class to have std::string, or std::vector, or std::map, or some other non-POD member type, or your Employee class contains pointer data, or if Employee contains just one virtual function, your current code will definitely not work correctly, and would more than likely crash. By properly serializing each member, then you don't have this risk.

    Regards,

    Paul McKenzie
    Last edited by Paul McKenzie; January 21st, 2006 at 12:37 PM.

  8. #8
    Join Date
    Mar 2005
    Posts
    40

    Re: comparing objects from a binary file

    Thank you very much I did read you initial posts and the topic you linked to but I still haven't grasped the idea perfectly. But now after your example I used the 2nd method which is as you said enough for me at the time being. But I still have some questions about the first method, I'll have to create separate entries for each private variable right? I can't like write the name and the grade for example to the same entry in the binary file right? I want them all to be in the same entry and not an entry for each variable. Please correct me if I'm wrong and I would appreciate it if you elaborate more on how I can write all fname, lname & grade in the same entry for each object.

    Thank you very much
    - Zifeer

  9. #9
    Join Date
    May 2005
    Location
    United States
    Posts
    526

    Re: comparing objects from a binary file

    Quote Originally Posted by zifeer
    But I still have some questions about the first method, I'll have to create separate entries for each private variable right? I can't like write the name and the grade for example to the same entry in the binary file right? I want them all to be in the same entry and not an entry for each variable.
    Perhaps I'm misunderstanding here, but I don't think what you're saying makes any sense. The concept of an "entry" in a binary file is exactly what you define it to be, nothing more. In other words, whether you write four variables to a file individually or write a struct containing four variables in a single operation is essentially irrelevant as far as the structure of the file is concerned. Writing the variables individually will require more individual lines of code, yes, but one way or the other, the file still contains information on x employees, represented as 4x values. The file does not contain a larger number of "entries" because you use a greater number of individual write operations to store the same amount of data. The data is still just the same string of ones and zeroes as far as your computer is concerned.

    Writing each variable individually is preferable, as the esteemed Mr. McKenzie has already explained in detail.

  10. #10
    Join Date
    Mar 2005
    Posts
    40

    Re: comparing objects from a binary file

    By entry I mean like I created a binary file and wrote to it 30 employee objects (which I call 30 entries). If I try and write every private variable of each object to the file, I then can't write to the binary file I created since it only accepts employee objects right? Now, using the second method suggested by Mr. Paul McKenzie, I can do that just fine while using the first method is what I didn't realy understand as on how to seekg/p and read/write the name or grade...etc
    Last edited by zifeer; January 23rd, 2006 at 07:02 PM.

  11. #11
    Ejaz's Avatar
    Ejaz is offline Elite Member Power Poster
    Join Date
    Jul 2002
    Location
    Lahore, Pakistan
    Posts
    4,211

    Re: comparing objects from a binary file

    [ Moved Thread ]

  12. #12
    Join Date
    May 2005
    Posts
    151

    Re: comparing objects from a binary file

    Quote Originally Posted by zifeer
    So I think the data is being read and wrote correctly, but the problem here seems to be that when I wrote to the 11th entry, all entries from 11 to 15 contained the same object. Why's that?
    That code may work now, but it will fail miserably when you add a virtual function to your class, or as Paul said, when your compiler uses a different structure alignment.

    Also, it's a bad idea to seek to an arbitrary point beyond the current end of file. You can't just seek to any position you want and start writing. If you need to increase the size of the file in order to write an entry at some previously-unused location, you have to manually fill in all the unused space before that.

    As for why the last 5 entries contain the same object, they may have been left over from previous writes to the file. Look into the ios::trunc flag.


    Quote Originally Posted by zifeer
    By entry I mean like I created a binary file and wrote to it 30 employee objects (which I call 30 entries). If I try and write every private variable of each object to the file, I then can't write to the binary file I created since it only accepts employee objects right?
    No, that's not right. Your code can write every data member to the file individually and treat that data as a single entry when seeking. If you define how the indivudal data members are read and written, then you define how large each entry is and therefore how far to seek for each entry.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured