473,406 Members | 2,387 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,406 software developers and data experts.

Working with binary files in C++

Hello

I'm a self instructed amateur attempting to read a huge file from disk... so
bear with me please... I just learned that reading a file in binary is
faster than text. So I wrote the following code that compiles OK. It runs and
shows the requested output. However, after execution, it pops one of those
windows to send error reports online to the porgram creator. I have managed
to find where the error is but can't see what's wrong. I'm posting the whole
code for context. I'm also marking where the problem is.

I appreciate your assistance. Thanks

#include <fstream>
#include <iostream>
#include <map>
using namespace std;

int main()
{
typedef map<int, double> IMAP;
IMAP Grid, NewGrid;

int IntValue1, rows = 3;
double DouValue2;

for(int i=0; i < rows; i++)
{
IntValue1 = i + 1;
DouValue2 = i * 2;
Grid.insert(IMAP::value_type(IntValue1, DouValue2));
}

IMAP::const_iterator IteratorG = Grid.begin();

cout << "Original Map" << endl;
while (IteratorG != Grid.end() )
{
cout << IteratorG->first << " " << IteratorG->second << endl;
IteratorG ++;
}

ofstream FileOut("C:/MyBinary.bin" , ios::binary);
FileOut.write((char*) &Grid, sizeof Grid);
FileOut.close();

// ******** PROBLEM IN HERE ******************
ifstream FileIn("C:/MyBinary.bin", ios::binary);
FileIn.read((char*) &NewGrid,sizeof NewGrid);
FileIn.close();
// *****************************************

IMAP::const_iterator NewIteratorG = NewGrid.begin();

cout << " " << endl;
cout << "New Map" << endl;
while (NewIteratorG != NewGrid.end() )
{
cout << NewIteratorG->first << " " << NewIteratorG->second << endl;
NewIteratorG ++;
}

return 0;
}

Nov 17 '05 #1
4 3653
knapak wrote:
Hello

I'm a self instructed amateur attempting to read a huge file from disk... so
bear with me please... I just learned that reading a file in binary is
faster than text.
However, writing in binary has a lot of potential problems, the main one
being that you can't write pointers, references or any non-POD types as
binary.

So I wrote the following code that compiles OK. It runs and shows the requested output. However, after execution, it pops one of those
windows to send error reports online to the porgram creator. I have managed
to find where the error is but can't see what's wrong. I'm posting the whole
code for context. I'm also marking where the problem is.

I appreciate your assistance. Thanks

#include <fstream>
#include <iostream>
#include <map>
using namespace std;

int main()
{
typedef map<int, double> IMAP;
IMAP Grid, NewGrid;

int IntValue1, rows = 3;
double DouValue2;

for(int i=0; i < rows; i++)
{
IntValue1 = i + 1;
DouValue2 = i * 2;
Grid.insert(IMAP::value_type(IntValue1, DouValue2));
}

IMAP::const_iterator IteratorG = Grid.begin();

cout << "Original Map" << endl;
while (IteratorG != Grid.end() )
{
cout << IteratorG->first << " " << IteratorG->second << endl;
IteratorG ++;
Prefer pre-increment where possible, since it can be faster:
++IteratorG;
}
The above would normally be a written as a for loop.

ofstream FileOut("C:/MyBinary.bin" , ios::binary);
FileOut.write((char*) &Grid, sizeof Grid);
Ok, the above just wrote out the internal structure of a map object.
This structure probably consists of pointers out to various nodes of the
map, such as the root, begin and end nodes, and probably a variable
holding the size of the map. So, as a guess, the above code is writing
out the values of three pointers to structures internal to the map, and
not one entry stored in the map is actually written out.
FileOut.close();

// ******** PROBLEM IN HERE ******************
ifstream FileIn("C:/MyBinary.bin", ios::binary);
FileIn.read((char*) &NewGrid,sizeof NewGrid);


The above is writing over the internal pointers and size stored in the
NewGrid object, which has undefined behaviour. You now have two
different map objects (Grid and NewGrid) that are sharing the same
internal data structures! This means that both Grid and NewGrid will
attempt to destroy the same structures when they go out of scope, which
will crash at best, and corrupt the heap in some more subtle way at worst.

In order to write out a map in either text or binary, you have to
iterate over the elements in the map and write them out one by one. You
are only allowed to binary read/write built in types, like int and
double, and C style structs that have no constructor/destructor or
private data. e.g. this is ok:

struct A
{
int a;
double b;
};

but std::pair (for example) is not. So to do the binary writing you need
to get down to the level of individual keys and values. e.g.

//write out the size, so we know how much to read in:
IMAP::size_type size = Grid.size();
//use reinterpret_cast to show we're doing something strange
FileOut.write(reinterpret_cast<char*>(&size), sizeof size);
for (IMAP::const_iterator i = Grid.begin();
i != end;
++i)
{
FileOut.write(
reinterpret_cast<char const*>(&i->first),
sizeof i->first);
FileOut.write(
reinterpret_cast<char const*>(&i->second),
sizeof i->second);
}

Finally, read them into a new map like this:
IMAP NewGrid;
IMAP::size_type size;
FileIn.read(reinterpret_cast<char*>(&size), sizeof size);
//now we know how many entries to read
for (IMAP::size_type i = 0; i < size; ++i)
{
int key;
double value;
FileIn.read(
reinterpret_cast<char*>(&key),
sizeof key);
FileIn.read(
reinterpret_cast<char*>(&value),
sizeof value);
//finally add it to the map
NewGrid.insert(IMAP::value_type(key, value));
}

Hopefully, that should do it, but note that I haven't compiled or tested
the code. As a final point, you should check the return value of every
call to read and write to make sure IO hasn't failed. You should also
note that binary files written as above generally aren't portable - you
won't be able to load the file using a PowerPC based MAC, for example.

Tom
Nov 17 '05 #2
Tom

Thank you so much for your help, this problem was driving me nuts!!!

A couple of things. The whole purpose of this code is to reduce the time to
load a big data file and load it into a map or multimap to be able to quickly
find a record in the maze of data. When I did it with text files it took a
grueling 40 minutes to read the file... yup only to read the file. Using
binaries was suggested to me to reduce the reading time by loading the data
in "one big chunk". I don't know if this is correct or not, but it certainly
reduced the time. Now your suggestion goes reading one record at a time...
mind me, your suggested code does work and takes only a few seconds to read
the data. Still, I wonder if those few seconds could still be somehow reduced
say from 8 to 4... I know I'm being ambitious, but I'd like to optimize this
part of the program as much as possible. If not, I'll be happy with this
solution.

The second question is related to your comment about portability. A file
saved as binary with this code in Windows cannot be read in UNIX? I thought
binary files could be read anywhere... Can this problem be solved? For
example, should I leave the data file as text (ASCII) and load it as binary
in the same amount of time? Can then the same file be read both in Windows
and UNIX?

Again thanks a million for you kind assistance!

Carlos

"Tom Widmer" wrote:
knapak wrote:
Hello

I'm a self instructed amateur attempting to read a huge file from disk... so
bear with me please... I just learned that reading a file in binary is
faster than text.


However, writing in binary has a lot of potential problems, the main one
being that you can't write pointers, references or any non-POD types as
binary.

So I wrote the following code that compiles OK. It runs and
shows the requested output. However, after execution, it pops one of those
windows to send error reports online to the porgram creator. I have managed
to find where the error is but can't see what's wrong. I'm posting the whole
code for context. I'm also marking where the problem is.

I appreciate your assistance. Thanks

#include <fstream>
#include <iostream>
#include <map>
using namespace std;

int main()
{
typedef map<int, double> IMAP;
IMAP Grid, NewGrid;

int IntValue1, rows = 3;
double DouValue2;

for(int i=0; i < rows; i++)
{
IntValue1 = i + 1;
DouValue2 = i * 2;
Grid.insert(IMAP::value_type(IntValue1, DouValue2));
}

IMAP::const_iterator IteratorG = Grid.begin();

cout << "Original Map" << endl;
while (IteratorG != Grid.end() )
{
cout << IteratorG->first << " " << IteratorG->second << endl;
IteratorG ++;


Prefer pre-increment where possible, since it can be faster:
++IteratorG;
}


The above would normally be a written as a for loop.

ofstream FileOut("C:/MyBinary.bin" , ios::binary);
FileOut.write((char*) &Grid, sizeof Grid);


Ok, the above just wrote out the internal structure of a map object.
This structure probably consists of pointers out to various nodes of the
map, such as the root, begin and end nodes, and probably a variable
holding the size of the map. So, as a guess, the above code is writing
out the values of three pointers to structures internal to the map, and
not one entry stored in the map is actually written out.
FileOut.close();

// ******** PROBLEM IN HERE ******************
ifstream FileIn("C:/MyBinary.bin", ios::binary);
FileIn.read((char*) &NewGrid,sizeof NewGrid);


The above is writing over the internal pointers and size stored in the
NewGrid object, which has undefined behaviour. You now have two
different map objects (Grid and NewGrid) that are sharing the same
internal data structures! This means that both Grid and NewGrid will
attempt to destroy the same structures when they go out of scope, which
will crash at best, and corrupt the heap in some more subtle way at worst.

In order to write out a map in either text or binary, you have to
iterate over the elements in the map and write them out one by one. You
are only allowed to binary read/write built in types, like int and
double, and C style structs that have no constructor/destructor or
private data. e.g. this is ok:

struct A
{
int a;
double b;
};

but std::pair (for example) is not. So to do the binary writing you need
to get down to the level of individual keys and values. e.g.

//write out the size, so we know how much to read in:
IMAP::size_type size = Grid.size();
//use reinterpret_cast to show we're doing something strange
FileOut.write(reinterpret_cast<char*>(&size), sizeof size);
for (IMAP::const_iterator i = Grid.begin();
i != end;
++i)
{
FileOut.write(
reinterpret_cast<char const*>(&i->first),
sizeof i->first);
FileOut.write(
reinterpret_cast<char const*>(&i->second),
sizeof i->second);
}

Finally, read them into a new map like this:
IMAP NewGrid;
IMAP::size_type size;
FileIn.read(reinterpret_cast<char*>(&size), sizeof size);
//now we know how many entries to read
for (IMAP::size_type i = 0; i < size; ++i)
{
int key;
double value;
FileIn.read(
reinterpret_cast<char*>(&key),
sizeof key);
FileIn.read(
reinterpret_cast<char*>(&value),
sizeof value);
//finally add it to the map
NewGrid.insert(IMAP::value_type(key, value));
}

Hopefully, that should do it, but note that I haven't compiled or tested
the code. As a final point, you should check the return value of every
call to read and write to make sure IO hasn't failed. You should also
note that binary files written as above generally aren't portable - you
won't be able to load the file using a PowerPC based MAC, for example.

Tom

Nov 17 '05 #3
knapak wrote:
Tom

Thank you so much for your help, this problem was driving me nuts!!!

A couple of things. The whole purpose of this code is to reduce the time to
load a big data file and load it into a map or multimap to be able to quickly
find a record in the maze of data. When I did it with text files it took a
grueling 40 minutes to read the file... yup only to read the file.Using
binaries was suggested to me to reduce the reading time by loading the data
in "one big chunk". I don't know if this is correct or not, but it certainly
reduced the time.
Unfortunately, std::map doesn't sit in memory in one large chunk - there
is one chunk for each entry in the map, so there is no way to write out
the map without iterating over the entries.

Now your suggestion goes reading one record at a time... mind me, your suggested code does work and takes only a few seconds to read
the data. Still, I wonder if those few seconds could still be somehow reduced
say from 8 to 4... I know I'm being ambitious, but I'd like to optimize this
part of the program as much as possible. If not, I'll be happy with this
solution.
I'm sure it is possible to reduce the time further. One approach is to
remove the calls to "read" and "write" and replace them with calls like
this:

FileOut.rdbuf()->sputn(same params as for write);

FileIn.rdbuf()->sgetn(same params as for read);

sputn/sgetn are quite a bit faster than write/read.

Another approach is to take the map and transfer its contents to a
vector, which can be written out in one chunk. I've posted two different
approaches, one legal but a bit slower, the other illegal, but likely to
work on most platforms:

typedef map<int, double> IMAP;

struct IMAP_POD
{
int key;
double value;
};

struct IMAPConverter
{
IMAP_POD operator()(IMAP::const_reference val) const
{
IMAP_POD p = {val.first, val.second};
return p;
}

std::pair<int, double> operator()(IMAP_POD const& val) const
{
return std::pair<int, double>(val.key, val.value);
}
};

void writeIMAP(IMAP const& m, ostream& os)
{
vector<IMAP_POD> v(m.size());
transform(m.begin(), m.end(), v.begin(), IMAPConverter());
//write the size:
vector<IMAP_POD>::size_type size = v.size();
os.write(reinterpret_cast<char*>(&size), sizeof size);
//write the map as a single vector:
os.write(reinterpret_cast<char*>(&v[0]), v.size() * sizeof v[0]);
}

void readIMAP(IMAP& m, istream& is)
{
vector<IMAP_POD>::size_type size;
//read the size:
is.read(reinterpret_cast<char*>(&size), sizeof size);
vector<IMAP_POD> v(size);
//read the map as a single vector:
is.read(reinterpret_cast<char*>(&v[0]), v.size() * sizeof v[0]);
vector<std::pair<int, double> > typedV;
typedV.reserve(size);
transform(v.begin(), v.end(), back_inserter(typedV), IMAPConverter());
//range insert for a sorted range
//is much faster than inserting one by one
m.insert(typedV.begin(), typedV.end());
}
Illegal approach:

typedef map<int, double> IMAP;

void writeIMAP(IMAP const& m, ostream& os)
{
typedef std::pair<int, double> non_const_value_type;
vector<non_const_value_type> v;
v.reserve(m.size());
v.insert(v.begin(), m.begin(), m.end());
//write the size:
vector<non_const_value_type>::size_type size = v.size();
os.write(reinterpret_cast<char*>(&size), sizeof size);
//write the map as a single vector:
os.write(reinterpret_cast<char*>(&v[0]), v.size() * sizeof v[0]);
}

void readIMAP(IMAP& m, istream& is)
{
typedef std::pair<int, double> non_const_value_type;
vector<non_const_value_type>::size_type size;
//read the size:
is.read(reinterpret_cast<char*>(&size), sizeof size);
vector<non_const_value_type> v(size);
//read the map as a single vector:
is.read(reinterpret_cast<char*>(&v[0]), v.size() * sizeof v[0]);
m.insert(v.begin(), v.end());
}

The reason that is illegal is that you can only copy the bytes into and
out of POD types, and std::pair<int, double> is not a POD type. However,
pair<int, double> is close to being a POD type (it doesn't have any base
classes or virtual functions, and the destructor is basically a no-op),
so the above is very likely to work on every platform.
The second question is related to your comment about portability. A file
saved as binary with this code in Windows cannot be read in UNIX?
The problem here is the format used by the CPU and compiler to hold ints
and doubles, and the sizes of those types. For example, some CPUs use a
big endian 64-bit 2s complement format for "int", while Windows (and
UNIX) compilers for x86 use a little endian 32-bit 2s-complement format.
Basically, the bits for a particular int value (such as 1234567) are
quite different on some platforms.

There's a bit more on it here:
http://www.eskimo.com/~scs/C-faq/q20.5.html

If you want portable binary, you need to decide on exactly the binary
format you want, and then make sure that your code writes out bytes in
the right format (byte-order swapping and padding as necessary).

I thought binary files could be read anywhere... Can this problem be solved? For
example, should I leave the data file as text (ASCII) and load it as binary
in the same amount of time? Can then the same file be read both in Windows
and UNIX?


It should be possible to optimize the code you use to read it as text so
that it operates much faster. If you need portability, this may be the
best option. If you want to do this, I'd suggest posting the code you
have (in a new thread) and asking for help in speeding it up.

Tom
Nov 17 '05 #4
Tom

Thanks again for your invaluable help. As for the alternative to write and
read, yes it improved the loading time... by about 0.4 of a sec (4.2 to
3.8)... which to me is quite good. I have to admit that your methods were
completely unknwon to me (remember I'm an amateur). I guess my only question
would be if there's is any room for problems by using the reinterpret_cast.

As for the protability problem, you actually suggested to explore some
alternatives of standardized binary formats including netCDF. Actually I've
tried using netCDF but didn't quite follow the procedures and is very
difficult to find people with the expertise to provide assistance. For now
I'll try to work with your solution and eventually when my files get bigger
and do require switching between windows and unix I'll come back and ask
directly if anyone knows how to work with netCDF.

I very much appreciate the time you took to help me.

Carlos

Nov 17 '05 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

10
by: Lorn Davies | last post by:
Hi there, I'm a Python newbie hoping for some direction in working with text files that range from 100MB to 1G in size. Basically certain rows, sorted by the first (primary) field maybe second...
9
by: Ching-Lung | last post by:
Hi all, I try to create a tool to check the delta (diff) of 2 binaries and create the delta binary. I use binary formatter (serialization) to create the delta binary. It works fine but the...
8
by: dagecko | last post by:
Hi I would like to know how to detect if a file is binary or not. It's important for me but I don't know where to start. Ty
10
by: joelagnel | last post by:
hi friends, i've been having this confusion for about a year, i want to know the exact difference between text and binary files. using the fwrite function in c, i wrote 2 bytes of integers in...
68
by: vim | last post by:
hello everybody Plz tell the differance between binary file and ascii file............... Thanks in advance vim
3
by: nicolasg | last post by:
Hi, I'm trying to open a file (any file) in binary mode and save it inside a new text file. After that I want to read the source from the text file and save it back to the disk with its...
15
by: JoeC | last post by:
I am writing a program that I am trying to learn and save binary files. This is the page I found as a source: http://www.angelfire.com/country/aldev0/cpphowto/cpp_BinaryFileIO.html I have...
9
by: deepakvsoni | last post by:
are binary files portable?
6
by: josequinonesii | last post by:
I've searched, I've read, I've tested and re-read numerous post but to no avail yet... Quite simply, the settings I've applied to my httpd.conf, httpd-vhost.conf and my hosts files simply does not...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.