Assistance with std::string in structures when doing binary read/write

Writing and reading structures containing std::string types to disk

Page 1 of 1

3 Replies - 2424 Views - Last Post: 06 July 2009 - 08:03 AM Rate Topic: -----

#1 ibbie  Icon User is offline

  • New D.I.C Head
  • member icon

Reputation: 2
  • View blog
  • Posts: 19
  • Joined: 07-July 08

Assistance with std::string in structures when doing binary read/write

Post icon  Posted 28 June 2009 - 02:56 PM

I have a (hopefully) quick question. I'm familiar with writing structures to disk in binary format using C, however I cannot seem to get it to work when the structure contains members of type std::string - or rather, writing appears to work great, but reading them out later results in a segmentation fault.

Here's the code I have thus far:

file structs.h
struct LinkHdr 
{
	int rec_count;
	std::string ftype;
};

struct Link
{
	int id;
	std::string title;
	std::string desc;
	std::string url;
	bool is_private;
};


file write.cc
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
#include <cstdio>
#include <libgen.h>

#include "structs.h"

int
main(int argc, char **argv)
{
	using namespace std;
	char * program = basename(argv[0]);
	fstream ofile;

	if (argc > 1)
	{
		LinkHdr header = {
			1,
			"Test",
		};

		Link bookmark = {
			1,
			"Slashdot",
			"News for nerds, stuff that matters",
			"http://www.slashdot.org/",
			false,
		};

		string filename = argv[1];

		ofile.open(filename.c_str(), ios_base::out | ios_base::binary);

		if(!ofile.is_open())
		{
			cerr << program << ": Can't open " << filename << " for output." << endl;
			exit(EXIT_FAILURE);
		}

		ofile.write( reinterpret_cast<char *>(&header), sizeof header);
		ofile.write( reinterpret_cast<char *>(&bookmark), sizeof bookmark);
		ofile.close();
	}
	else
	{
		cout << "Usage: " << program << " <filename>" << endl;
		return EXIT_FAILURE;
	}
	
	return EXIT_SUCCESS;
}



file read.cc
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
#include <libgen.h>

#include "structs.h"

int
main(int argc, char **argv)
{
	using namespace std;
	char * program = basename(argv[0]);
	ifstream ifile;

	if (argc > 1)
	{
		LinkHdr header; 
		Link bookmark;

		string filename = argv[1];

		ifile.open(filename.c_str(), ios::in | ios::binary);

		if(ifile.is_open())
		{
			ifile.seekg(0);
			cout << "file: " << filename << endl;

			while(ifile.read(reinterpret_cast<char *>(&header), sizeof header))
			{
				cout << "count: " << header.rec_count << "\n"
					 << "type: " << header.ftype << endl;
				break;
			}

			while(ifile.read( (char *) &bookmark, sizeof bookmark))
			{
				cout << "title: " << bookmark.title << "\n"
					 << "description: " << bookmark.desc << "\n"
					 << "url: " << bookmark.url << "\n" 
					 << "is_private: " << bookmark.is_private << endl;
			}
			ifile.close();
		}
	}
	else
	{
		cout << "Usage: " << program << " <filename>" << endl;
		return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}


A couple of things to note:
  • I know it'd be easier to just use an SQLite3 db, like Firefox does. I just want to learn how to do it the bad old way. :D
  • It works just fine when I use a simple data type, like char blah[400];
Many thanks in advance!

This post has been edited by ibbie: 28 June 2009 - 02:58 PM


Is This A Good Question/Topic? 0
  • +

Replies To: Assistance with std::string in structures when doing binary read/write

#2 #define  Icon User is offline

  • Duke of Err
  • member icon

Reputation: 1276
  • View blog
  • Posts: 4,396
  • Joined: 19-February 09

Re: Assistance with std::string in structures when doing binary read/write

Posted 28 June 2009 - 04:45 PM

Hi, yes the string class seems to only hold a pointer to the data,
as well as other info. So it's back to char strings I think.
You can use c_str method to access the string.

c_str

Can't think of any other way. You would need a fixed size anyway.
Was This Post Helpful? 1
  • +
  • -

#3 ibbie  Icon User is offline

  • New D.I.C Head
  • member icon

Reputation: 2
  • View blog
  • Posts: 19
  • Joined: 07-July 08

Re: Assistance with std::string in structures when doing binary read/write

Posted 05 July 2009 - 07:19 PM

View Post#define, on 28 Jun, 2009 - 03:45 PM, said:

Hi, yes the string class seems to only hold a pointer to the data,
as well as other info. So it's back to char strings I think.
You can use c_str method to access the string.

c_str

Can't think of any other way. You would need a fixed size anyway.


Sorry for taking so long to get back about this. That makes sense, when I think about it. Doh!

I guess I'll just cheat and either write a binding to sqlite3 or use Poco's binding, depending on how complex I need it to be. I'm not a big fan of fixed size, and was hoping there was something I was missing, being newish to C++. That is, coming from C, then seeing all kinds of "magic" (I use the term lightly) via the STL - vectors, for instance, blow my old dynamic arrays out of the water in terms of speed - I didn't want to assume that what I was trying to do was impossible.

Thanks! :D
Was This Post Helpful? 0
  • +
  • -

#4 perfectly.insane  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 70
  • View blog
  • Posts: 644
  • Joined: 22-March 08

Re: Assistance with std::string in structures when doing binary read/write

Posted 06 July 2009 - 08:03 AM

It's not that you can't use a std::string. You can, but you must write the actual data, and unless you plan on trying to use fixed sized strings, you must write string length information to the file as well. For example:


void write_link_to_stream(const Link& link, ostream& ostr)
{
    // Write id
    ostr.write(reinterpret_cast<char*>(&link.id), sizeof(link.id));

    // Write a string item
    int len = link.title.size();
    ostr.write(reinterpret_cast<char*>(&len), sizeof(len));
    ostr.write(const_cast<char*>(link.title.c_str()), len);
    
    // ... Repeat for each string item in link
}

void read_link_from_stream(Link& link, istream& istr)
{
    istr.read(reinterpret_cast<char*>(&link.id), sizeof(link.id));

    // Read a string item
    int len;
    istr.read(reinterpret_cast<char*>(&len), sizeof(len));
    link.title.resize(len);
    istr.read(const_cast<char*>(link.title.c_str()), len);
    
    // Read the rest of the string items
}




While actually modifying the memory pointed to by c_str() in read_link_from_stream may not be considered The Right Thing To Do™, it should be safe, provided that one calls resize prior and never copies more bytes into the buffer returned by c_str() than the string actually is set to contain.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1