Thread: binary read/write of objects

  1. #16
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >I was unaware of any hidden pitfalls of using read() and write().
    They aren't hidden, just subtle. Here's a taste of some of the issues you need to consider when using a binary file.

    Pitfall #1 (the one I was referring to): Put simply, unless you can directly convert an object to a pointer to unsigned char without invoking undefined behavior, binary I/O using that object is also undefined. Any POD type can be safely cast to a pointer to unsigned char; this is guaranteed by the standard. If you don't know whether your class is POD or not then it probably isn't, and binary I/O is undefined.

    Pitfall #2: Binary I/O is a shallow copy. If your object (assuming it's POD) has pointers to dynamic memory then you'll be copying addresses, not the content of those addresses.

    Pitfall #3: Binary I/O is not portable at all. You can write to a file on one platform and fail to read it on another platform (even a different version of the same platform). Even worse, differen implementations or versions of the same implementation can fail to handle the file properly if you mix them.
    My best code is written with the delete key.

  2. #17
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Of course, if your object is not POD, but you can convert it to some representation using only POD types, then you can get around pitfalls #1 & #2. For example, as has been said already, converting a C++ string to its length and its character data before outputting that information in binary, and then converting it back into a string after reading it in, is a legitimate way to read/write a string object in binary.

  3. #18
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    Well, you could just use a plain ole char array, and if it's part of a struct, read and write sizeof(struct).
    Why not just write the char array in text mode and read it back in text mode?

  4. #19
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Quote Originally Posted by 7stud
    Why not just write the char array in text mode and read it back in text mode?
    Because the question was how to do it in binary mode.

  5. #20
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >is a legitimate way to read/write a string object in binary
    Yes, as I said in my first post. But the OP apparently wanted to do it directly, without the intermediate step of converting the object to something safe, which is all kinds of wrong.
    My best code is written with the delete key.

  6. #21
    ... arjunajay's Avatar
    Join Date
    May 2005
    Posts
    203

    Talking ok

    I have have decided to avoid all this ambiguity and arguments simply by going back to the normal read / write.
    P.S.(I have to since I'm sTuck on a filesize problem)
    I take back what I just said!!!
    I got help in correcting a function whose argument I mixed up.
    Binary is allmost all OK now.
    Last edited by arjunajay; 06-04-2005 at 05:17 AM. Reason: stupidness from my part.

  7. #22
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >Binary is allmost all OK now.
    How are you doing it now? Just to get a concrete view, can you show us your class declaration and how you write objects to the file? It's possible that it really is okay, but it's also possible that it's still undefined, but hasn't broken loudly yet.
    My best code is written with the delete key.

  8. #23
    ... arjunajay's Avatar
    Join Date
    May 2005
    Posts
    203
    My code isn't very pretty; in fact you might just pass out seeing its horrible state.
    Any way I will post it afte I finish a certain function(Which obviously creates problems!).

  9. #24
    ... arjunajay's Avatar
    Join Date
    May 2005
    Posts
    203

    Angry code

    I edited this post cause I found a stupid mistake in my person class.
    This is my program. not all functions have been implemnted.
    Don't take this thing very seriously, since I have abandoned the same project tons of times due I/O problems. Whenever theres a cin any where in the program before a cin.getline() it is ignored by the program, ie, the program does not wait for input. also the name is not copied on to the object; only the number is copied.
    don't know why.
    You were right; binary isn't my cup o' tea.
    The class.
    Code:
    class Person{
    private:
    char name[30], numb[15];
    
    public:
    Person(){
    	name[0] = '\0';
    	numb[0] = '\0';
    }
    char* get_name(){ return name; }
    char* get_numb(){ return numb; }
    void set(char*, char*);
    };
    
    void Person::set(char* na, char* n1){
    for(int i=0; na[ i] !='\0'; i++)name[ i] = na[ i];	//This was edited and the next line added.
    name[ i] = '\0';
    for(i=0; n1[ i] !='\0'; i++)numb[ i] = n1[ i];
    numb[ i] = '\0';
    }
    program.
    Code:
    //Note the size of buffer used to read all the records.
    //It might need expansion or some thing.
    //deletion of entries not implemented.
    //edit of entries not implemented.
    
    const char *substr(const char *match, const char *s); //remember me?
    void list_record(Person*, int);
    void search_record(Person*, int);
    void add_record(Person*, int&);
    void delete_record(Person*, int&);
    void edit_record(Person*, int);
    void refresh_file(Person*, int, fstream &);
    long read_from_file(Person*, fstream &); //transfer all records to Person*.
    
    int main(){
    clrscr();
    fstream myfile;
    
    myfile.open("Numbers.imp", ios::in | ios::binary | ios::app);
    if(myfile.fail())cout<<"E R R O R Opening File !\n";
    else{
    	Person buffer[30];
    	int size = read_from_file(buffer, myfile); //every thing is in buffer.
    	int n = size/sizeof(Person);
    	char ch;
    
    	cout<<"No: of saved records :"<<n<<endl;
    	myfile.close(); //Let's close it for now...
    	do{
    	cout<<"\n<< M E N U >>\n"
    		<<"1) List ('l')"<<endl
    		<<"2) Search ('s')"<<endl
    		<<"3) Add New Entry ('n')"<<endl
    		<<"4) Delete an entry ('d'){not implemented yet}"<<endl
    		<<"5) Edit an entry ('e') {n i y}"<<endl
    		<<"6) Quit ('q')"<<endl
    		<<"Your Choice :";
    		cin>>ch;		//I think this cin causes cin.getline() problems.
    		//cause when I changed it to getche(), the program waited for input
    		//but name was'nt copied to object still.
    		switch(ch){
    		case 'l':list_record(buffer, n);
    			break;
    		case 's':search_record(buffer, n);
    			break;
    		case 'n':add_record(buffer, n);
    			break;
    		case 'd':delete_record(buffer, n);
    			break;
    		case 'e':edit_record(buffer, n);
    			break;
    		case 'q':refresh_file(buffer, n, myfile);
    		cout<<"Records updated...\n\n\n"
    			<<"Exiting...\n\n\nPress any key...";
    			getch();
    			exit(0);
    		default :cout<<"Invalid Choice !!!\n";
    		}
    		}while(1);
    }
    return 0;
    }
    //Is there a file size function???below
    long read_from_file(Person* buff, fstream &file){
    	file.seekg(0, ios::beg); file.seekp(0, ios::beg);
    	long start = file.tellg(), end, size;
    	file.seekg(0, ios::end);
    	end = file.tellg();
    	size = end-start;
    	file.seekg(0, ios::beg);
    	file.read((unsigned char*) &buff, size); //Copies records to buffer.
    	return size;
    }
    
    void list_record(Person* buff, int n){
    if(n){
    	cout<<endl;
    	for(int i=0; i<n; i++)
    	cout<<i+1<<") "<<buff[ i].get_name()<<"\t\t\t"<<buff[ i].get_numb()<<endl;
    	for(i=0; i<20; i++)cout<<"==";
    	cout<<endl;
    }
    }
    
    void search_record(Person* buff, int n){
    char na[30];
    cout<<"Enter the name or a part of the name :";
    cin.getline(na, 30);                //This isn't working...
    for(int i=0; i<n; i++)
    	if(substr(na, buff[ i].get_name()) )
    	cout<<i+1<<") "<<buff[ i].get_name()<<"\t\t\t"<<buff[ i].get_numb()<<endl;
    
    for(i=0; i<20; i++)cout<<"==";
    cout<<endl;
    }
    
    void add_record(Person* buff, int& n){
    char name[30], number[15];
    cout<<"Enter the name    :";
    cin.getline(name, 30);          //This isn't working... program not waiting for input.
    cout<<"Enter the number  :";	//The program jumps to this line instead
    cin.getline(number, 15);        //But here the machine DOES wait for input.
    	//I think the problem is in the 'cin>>ch;' in the
    	//previous 'switch'. but dont know why.
    Person a;
    a.set(name, number);
    buff[ n]=a;
    n++;
    cout<<"Record added :"<<endl;
    }
    Last edited by arjunajay; 06-05-2005 at 04:35 AM. Reason: [b]Found an error later.[/b]

  10. #25
    Registered User
    Join Date
    Mar 2002
    Posts
    1,595
    Mixing getline() and >> in the same program often causes this problem. The problem arises because, for the most part, input in C++ is buffered. That is, input goes from the keyboard to a buffer before it is placed into variables. Istream methods extract the information from the input buffer and attempt to place into the variable(s) indicated in the code. Each istream method works a little differently, giving you a lot of flexibility, as well as a few headaches as you try to figure them all out. In this case, >> doesn't remove the terminating whitespace char. The newline char is considered a whitespace char. >> is frequently terminated by newline char (when you push the enter key a newline char is entered into the input buffer). The default terminating char is also the newline char. Therefore if you call >> before getline() and the call to >> is terminated by the newline char, the newline char will remain in the input buffer. When getline() goes to retrieve information from the input buffer the first thing it sees is the newline char and ends up not putting anything except the terminating null char in the desired char array. You don't even have to enter anything because the newline char is already there. The program just "magically" skips right over the getline() call. To deal with this you need to call ignore() before each call to getline(). The best way is to go all out and ignore as many char as could possibly be stored in the input buffer, although usually just ignoring 1 char is enough.

    There are functions in the cstring (or string.h if your compiler doesn't have cstring) header file that allows you to copy strings one to another without your having to loop through the string to copy copying, one char at a time. You could look them up if you want. A common one to use is strcpy(). Your loop works fine, but most people will use strcpy.

    Try this. Put a line like this:

    cout << "sizeof Person" << endl;

    before this line:

    int n = size/sizeof(Person);

    and see what you get. I think it might surprise you. Ithink you would be better served by making name and numb member variables of Person, where they belong, rather than global variables, and then writing their value to file with << and reading from file with >> rather than trying to use binary read/write.
    You're only born perfect.

  11. #26
    ... arjunajay's Avatar
    Join Date
    May 2005
    Posts
    203
    Thanks a lot for the buffer info.
    I did'nt use strcpy because then i would have include the complete header file containing other functions. I would then be wasting filesize for a measly srtring copy function. Any way I was looking for a file size returning function.
    To get rid of this buffer thingi, cant I use cout<<flush; or cin>>flush; (I heard that this empties the buffer; but I'm not sure).
    I'm trying to build another version which uses the normal( '>>', '<<' ) file I/O.
    Try this. Put a line like this:

    cout << "sizeof Person" << endl;

    before this line:

    int n = size/sizeof(Person);
    How can that line you suggested help?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Binary Search Trees Part III
    By Prelude in forum A Brief History of Cprogramming.com
    Replies: 16
    Last Post: 10-02-2004, 03:00 PM
  2. Request for comments
    By Prelude in forum A Brief History of Cprogramming.com
    Replies: 15
    Last Post: 01-02-2004, 10:33 AM
  3. File Encryption & Read/Write in Binary Mode
    By kuphryn in forum C++ Programming
    Replies: 5
    Last Post: 11-30-2001, 06:45 PM
  4. storing string objects to binary files
    By Unregistered in forum C++ Programming
    Replies: 2
    Last Post: 10-06-2001, 11:33 PM