The project I'm working on, as a custom file format consisting of the header of a few different variables, followed by the pixel data. My colleagues have developed a GUI, where processing, writing reading and displaying this type of file format works fine.
But my problem is, while I have assisted in writing the code for writing data to disk, I cannot myself read this kind of file and get satisfactorily values back. I am able to read the first variable back (char array) but not the following value(s).
So the file format matches the following structure:
typedef struct {
char hxtLabel[8];
u64 hxtVersion;
int motorPositions[9];
int filePrefixLength;
char filePrefix[100];
..
} HxtBuffer;
In the code, I create an object of the above structure and then set these example values:
setLabel("MY_LABEL");
setFormatVersion(3);
setMotorPosition( 2109, 5438, 8767, 1234, 1022, 1033, 1044, 1055, 1066);
setFilePrefixLength(7);
setFilePrefix( string("prefix_"));
setDataTimeStamp( string("000000_000000"));
My code for opening the file:
// Open data file, binary mode, reading
ifstream datFile(aFileName.c_str(), ios::in | ios::binary);
if (!datFile.is_open()) {
cout << "readFile() ERROR: Failed to open file " << aFileName << endl;
return false;
}
// How large is the file?
datFile.seekg(0, datFile.end);
int length = datFile.tellg();
datFile.seekg(0, datFile.beg);
cout << "readFile() file " << setw(70) << aFileName << " is: " << setw(15) << length << " long\n";
// Allocate memory for buffer:
char * buffer = new char[length];
// Read data as one block:
datFile.read(buffer, length);
datFile.close();
/// Looking at the start of the buffer, I should be seeing "MY_LABEL"?
cout << "buffer: " << buffer << " " << *(buffer) << endl;
int* mSSX = reinterpret_cast<int*>(*(buffer+8));
int* mSSY = reinterpret_cast<int*>(&buffer+9);
int* mSSZ = reinterpret_cast<int*>(&buffer+10);
int* mSSROT = reinterpret_cast<int*>(&buffer+11);
int* mTimer = reinterpret_cast<int*>(&buffer+12);
int* mGALX = reinterpret_cast<int*>(&buffer+13);
int* mGALY = reinterpret_cast<int*>(&buffer+14);
int* mGALZ = reinterpret_cast<int*>(&buffer+15);
int* mGALROT = reinterpret_cast<int*>(&buffer+16);
int* filePrefixLength = reinterpret_cast<int*>(&buffer+17);
std::string filePrefix; std::string dataTimeStamp;
// Read file prefix character by character into stringstream object
std::stringstream ss;
char* cPointer = (char *)(buffer+18);
int k;
for(k = 0; k < *filePrefixLength; k++)
{
//read string
char c;
c = *cPointer;
ss << c;
cPointer++;
}
filePrefix = ss.str();
// Read timestamp character by character into stringstream object
std::stringstream timeStampStream;
/// Need not increment cPointer, already pointing # 1st char of timeStamp
for (int l= 0; l < 13; l++)
{
char c;
c = * cPointer;
timeStampStream << c;
}
dataTimeStamp = timeStampStream.str();
cout << 25 << endl;
cout << " mSSX: " << mSSX << " mSSY: " << mSSY << " mSSZ: " << mSSZ;
cout << " mSSROT: " << mSSROT << " mTimer: " << mTimer << " mGALX: " << mGALX;
cout << " mGALY: " << mGALY << " mGALZ: " << mGALZ << " mGALROT: " << mGALROT;
Finally, what I see is here below. I added the 25 just to double check that not everything was coming out in hexadecimal. As you can see, I am able to see the label "MY_LABEL" as expected. But the 9 motorPositions all come out looking suspiciously like addresses are not values. The file prefix and the data timestamp (which should be strings, or at least characters), are just empty.
buffer: MY_LABEL M
25
mSSX: 0000000000000003 mSSY: 00000000001BF618 mSSZ: 00000000001BF620 mSSROT: 00000000001BF628 mTimer: 00000000001BF630 mGALX: 00000000001BF638 mGALY: 00000000001BF640 mGALZ: 00000000001BF648 mGALROT: 00000000001BF650filePrefix: dataTimeStamp:
I'm sure the solution can't be too complicated, but I reached a stage where I had this just spinning and I cannot make sense of things.
Many thanks for reading this somewhat long post.
-- Edit--
I might hit the maximum length allowed for a post, but just in case I thought I shall post the code that generates the data that I'm trying to read back:
bool writePixelOutput(string aOutputPixelFileName) {
// Write pixel histograms out to binary file
ofstream pixelFile;
pixelFile.open(aOutputPixelFileName.c_str(), ios::binary | ios::out | ios::trunc);
if (!pixelFile.is_open()) {
LOG(gLogConfig, logERROR) << "Failed to open output file " << aOutputPixelFileName;
return false;
}
// Write binary file header
string label("MY_LABEL");
pixelFile.write(label.c_str(), label.length());
pixelFile.write((const char*)&mFormatVersion, sizeof(u64));
// Include File Prefix/Motor Positions/Data Time Stamp - if format version > 1
if (mFormatVersion > 1)
{
pixelFile.write((const char*)&mSSX, sizeof(mSSX));
pixelFile.write((const char*)&mSSY, sizeof(mSSY));
pixelFile.write((const char*)&mSSZ, sizeof(mSSZ));
pixelFile.write((const char*)&mSSROT, sizeof(mSSROT));
pixelFile.write((const char*)&mTimer, sizeof(mTimer));
pixelFile.write((const char*)&mGALX, sizeof(mGALX));
pixelFile.write((const char*)&mGALY, sizeof(mGALY));
pixelFile.write((const char*)&mGALZ, sizeof(mGALZ));
pixelFile.write((const char*)&mGALROT, sizeof(mGALROT));
// Determine length of mFilePrefix string
int filePrefixSize = (int)mFilePrefix.size();
// Write prefix length, followed by prefix itself
pixelFile.write((const char*)&filePrefixSize, sizeof(filePrefixSize));
size_t prefixLen = 0;
if (mFormatVersion == 2) prefixLen = mFilePrefix.size();
else prefixLen = 100;
pixelFile.write(mFilePrefix.c_str(), prefixLen);
pixelFile.write(mDataTimeStamp.c_str(), mDataTimeStamp.size());
}
// Continue writing header information that is common to both format versions
pixelFile.write((const char*)&mRows, sizeof(mRows));
pixelFile.write((const char*)&mCols, sizeof(mCols));
pixelFile.write((const char*)&mHistoBins, sizeof(mHistoBins));
// Write the actual data - taken out for briefy sake
// ..
pixelFile.close();
LOG(gLogConfig, logINFO) << "Written output histogram binary file " << aOutputPixelFileName;
return true;
}
-- Edit 2 (11:32 09/12/2015) --
Thank you for all the help, I'm closer to solving the issue now. Going with the answer from muelleth, I try:
/// Read into char buffer
char * buffer = new char[length];
datFile.read(buffer, length);// length determined by ifstream.seekg()
/// Let's try HxtBuffer
HxtBuffer *input = new HxtBuffer;
cout << "sizeof HxtBuffer: " << sizeof *input << endl;
memcpy(input, buffer, length);
I can then display the different struct variables:
qDebug() << "Slice BUFFER label " << QString::fromStdString(input->hxtLabel);
qDebug() << "Slice BUFFER version " << QString::number(input->hxtVersion);
qDebug() << "Slice BUFFER hxtPrefixLength " << QString::number(input->filePrefixLength);
for (int i = 0; i < 9; i++)
{
qDebug() << i << QString::number(input->motorPositions[i]);
}
qDebug() << "Slice BUFFER filePrefix " << QString::fromStdString(input->filePrefix);
qDebug() << "Slice BUFFER dataTimeStamp " << QString::fromStdString(input->dataTimeStamp);
qDebug() << "Slice BUFFER nRows " << QString::number(input->nRows);
qDebug() << "Slice BUFFER nCols " << QString::number(input->nCols);
qDebug() << "Slice BUFFER nBins " << QString::number(input->nBins);
The output is then mostly as expected:
Slice BUFFER label "MY_LABEL"
Slice BUFFER version "3"
Slice BUFFER hxtPrefixLength "2"
0 "2109"
1 "5438"
...
7 "1055"
8 "1066"
Slice BUFFER filePrefix "-1"
Slice BUFFER dataTimeStamp "000000_000000P"
Slice BUFFER nRows "20480"
Slice BUFFER nCols "256000"
Slice BUFFER nBins "0"
EXCEPT, dataTimeStamp, which is 13 chars long, displays instead 14 chars. The 3 variables that follow: nRows, nCols and nBins are then incorrect. (Should be nRows=80, nCols=80, nBins=1000). My guess is that the bits belonging to the 14th char of dataTimeStamp should be read along with nRows, and so cascade on to produce the correct nCols and nBins.
I have separately verified (not shown here) using qDebug that what I'm writing into the file, really are the values I expect, and their individual sizes.
I personally would try to read exactly the number of bytes your struct is from the file, i.e. something like
int length = sizeof(HxtBuffer);
and then simply use memcpy to assign a local structure from the read buffer:
HxtBuffer input;
memcpy(&input, buffer, length);
You can then access your data e.g. like:
std::cout << "Data: " << input.hxtLabel << std::endl;
Why do you read to buffer, instead of using the structure for reading?
HxtBuffer data;
datFile.read(reinterpret_cast<char *>(&data), sizeof data);
if(datFile && datFile.gcount()!=sizeof data)
throw io_exception();
// Can use data.
If you want to read to a chracter buffer, than your way of getting the data is just wrong. You probably want to do something like this.
char *buf_offset=buffer+8+sizeof(u64); // Skip label (8 chars) and version (int64)
int mSSX = *reinterpret_cast<int*>(buf_offset);
buf_offset+=sizeof(int);
int mSSY = *reinterpret_cast<int*>(buf_offset);
buf_offset+=sizeof(int);
int mSSZ = *reinterpret_cast<int*>(buf_offset);
/* etc. */
Or, a little better (provided you don't change the contents of the buffer).
int *ptr_motors=reinterpret_cast<int *>(buffer+8+sizeof(u64));
int &mSSX = ptr_motors[0];
int &mSSY = ptr_motors[1];
int &mSSZ = ptr_motors[2];
/* etc. */
Notice that I don't declare mSSX, mSSY etc. as pointers. Your code was printing them as addresses because you told the compiler that they were addresses (pointers).
Related
I am trying to write int61_t data to a ply file (a text file with a special header). I a piece of code that does this with a time consuming loop I am trying to speed it up.
I want to avoid the time spent iterating through the array by putting my data directly into ofs.write. Can I do this?
This is what I've tried
The functional, but slow, code is as follows:
int width = point_cloud_image.get_width_pixels();
int height = point_cloud_image.get_height_pixels();
int i_max = width * height;
// get data
int16_t* point_cloud_image_data = (int16_t*)(void*)point_cloud_image.get_buffer();
std::stringstream ss;
for (int i = 0; i < i_max; i++) // executes 921600 times - this is the bottleneck
{
ss << point_cloud_image_data[3 * i + 0\] << " " << point_cloud_image_data[3 * i + 1\] << " " << point_cloud_image_data[3 * i + 2] << "\n";
}
// save to the ply file
std::ofstream ofs("myfile.ply", std::ios::out | std::fstream::binary); // text mode first
ofs << "ply\n" << "format ascii 1.0\n" << "element vertex" << " " << i_max << "\n" << "property float x\n" << "property float y\n" << "property float z\n" << "end_header\n" << std::endl;
ofs.write(ss.str().c_str(), (std::streamsize)ss.str().length());
ofs.close();
I want to avoid the time spent iterating through the array by putting my point_cloud_image_data pointer directly into ofs.write. My code to do that looks like this:
int width = point_cloud_image.get_width_pixels();
int height = point_cloud_image.get_height_pixels();
int i_max = width * height;
// get data
int16_t* point_cloud_image_data = (int16_t*)(void*)point_cloud_image.get_buffer();
// save to the ply file
std::ofstream ofs("myfile.ply", std::ios::out | std::fstream::binary); // text mode first
ofs << "ply\n" << "format ascii 1.0\n" << "element vertex" << " " << i_max << "\n" << "property float x\n" << "property float y\n" << "property float z\n" << "end_header\n" << std::endl;
ofs.write((char*)(char16_t*)point_cloud_image_data, i_max);
ofs.close();
This is a lot faster, but now point_cloud_image_data is written in binary (the file contains characters like this: ¥ûú). How can I write the array to the text file without a time consuming loop?
Integers are stored in the computer in binary representation. To write an array of integer values to a text file, each one needs to be converted to a series of decimal digits. So you're going to need a loop. Even with buffering and compiler optimizations enabled, conversion of binary to text and back will inevitably be slower than directly working with binary data.
But if all you care about is raw performance, the PLY format can actually be binary. So your second attempt might actually work and produce a working (albeit non-human-readable) PLY file.
int width = point_cloud_image.get_width_pixels();
int height = point_cloud_image.get_height_pixels();
int i_max = width * height;
std::ofstream ofs("myfile.ply", std::ios::out | std::fstream::binary);
ofs << "ply\n"
<< (is_little_endian
? "format binary_little_endian 1.0\n"
: "format binary_big_endian 1.0\n")
<< "element vertex " << i_max << "\n"
<< "property short x\n"
<< "property short y\n"
<< "property short z\n"
<< "end_header\n";
ofs.write((const char*)point_cloud_image.get_buffer(), i_max * 2 * 3);
ofs.close();
The is_little_endian check is optional and can be omitted, but it makes the code a little bit more portable.
int num = 1;
bool is_little_endian = *(char *)&num == 1;
I'm reading blocks of data from the file, but not all at once (ex. 3 bytes per read/write) and then write same 3 bytes back to file to the very same position inside a file, and then continue looping until there are no more blocks to read.
In other words I'm trying to rewrite the file by it's very contents.
However there is a problem that final output isn't the same as it was in the beginning.
Following sample code reads 3 bytes per iteration from a file "sample.txt", file contents are simple:
0123456789
after reading data and writing data back to file, the contents are:
012345345345
As you see data doesn't get rewritten correctly for some reason.
#include <fstream>
#include <iostream>
using namespace std;
#define BLOCK_SIZE 3
int main()
{
// open file
fstream file;
file.open("sample.txt", ios::binary | ios::out | ios::in);
// determine size and number of blocks to read
file.seekg(0, ios::end);
streampos size = file.tellg();
int blocks = size / BLOCK_SIZE;
cout << "size:\t" << size << endl;
if (size % BLOCK_SIZE != 0)
{
++blocks;
}
cout << "blocks:\t" << blocks << endl;
// return to beginning
file.seekg(ios::beg);
// we will read data here
unsigned char* data = new unsigned char[BLOCK_SIZE];
streampos pos;
// read blocks of data and write data back
for (int i = 0; i < blocks; ++i)
{
pos = file.tellg();
cout << "before read:\t" << pos << endl;
// read block
file.read(reinterpret_cast<char*>(data), BLOCK_SIZE);
cout << "after read:\t" << file.tellg() << endl;
// write same block back to same position
file.seekp(pos);
cout << "before write:\t" << file.tellg() << endl;
file.write(reinterpret_cast<char*>(data), BLOCK_SIZE);
cout << "after write:\t" << file.tellg() << endl;
// reset buffer
memset(data, 0, BLOCK_SIZE);
}
file.close();
delete[] data;
cin.get();
return 0;
}
Do you see what could be the reason for bad overwrite?
EDIT:
Sorry, I can't see how the linked duplicate answers my question, I'm simply unable to apply given answer to the code above.
Your code does not handle the EOF condition well, and leaves the stream in a bad state after trying to read past the end of the file. On my system, this results in all further calls to the stream having no effect. I bet that isn't the case on your system (which I suspect is a bug in its iostream implementation). I re-did your code to handle the EOF condition correctly, and also to be a lot cleaner in a few other ways:
#include <fstream>
#include <iostream>
using namespace std;
const int BLOCK_SIZE = 3;
int main()
{
// open file
fstream file;
file.open("sample.txt", ios::binary | ios::out | ios::in);
// we will read data here
bool found_eof = false;
// read blocks of data and write data back
while (!found_eof)
{
unsigned char data[BLOCK_SIZE] = {0};
char * const data_as_char = reinterpret_cast<char *>(data);
streampos const pos = file.tellp();
int count_to_write = BLOCK_SIZE;
cout << "before read:\t" << file.tellg() << ' ' << pos << '\n';
// read block
if (!file.read(data_as_char, BLOCK_SIZE)) {
found_eof = true;
count_to_write = file.gcount();
file.clear();
cout << "Only " << count_to_write << " characters extracted.\n";
}
cout << "after read:\t" << file.tellg() << ' ' << file.tellp() << '\n';
// write same block back to same position
file.seekp(pos);
cout << "before write:\t" << file.tellg() << ' ' << file.tellp() << '\n';
file.write(data_as_char, count_to_write);
cout << "after write:\t" << file.tellg() << ' ' << file.tellp() << '\n';
file.seekp(file.tellp());
}
file.close();
cin.get();
return 0;
}
But, this is not fundamentally different. Both versions work for me just the same. I'm on Linux with g++.
From the linked to possible dupe, I would also suggest adding this just before the closing } of your for loop:
file.seekp(file.tellp());
I've put that in my code in the appropriate place.
Using YAML::Emitter I sometimes get YAML::Emitter.size() to return a bigger size than strlen(YAML::Emitter.c_str()).
e.g.
YAML::Emitter em;
em << something;
if (em.size() > strlen(em.c_str())
{
std::cout << "WOW " << em.size() << " > " << strlen(em.c_str());
}
This creates issues when writing the yaml text to a buffer:
char* buff = malloc(em.size());
memcpy(buff, em.c_str(), em.size());
because the extra characters at the end fail the yaml parser on the other side that reads this buffer.
Thanks!
I'm trying to write a program that reads in an OpenGL shader from a .txt file. I've actually already done this a few days ago, this was the code I used:
char vShaderData[2000];
char fShaderData[2000];
void readShaders() {
std::ifstream vShaderF;
std::ifstream fShaderF;
vShaderF.open("shaders//vertexShader.txt");
fShaderF.open("shaders//fragShader.txt");
if (vShaderF.is_open() && fShaderF.is_open()) std::cout << m << "Shader read success" << std::endl;
else std::cout << "Shader read fail" << std::endl;
std::cout << m << "vertex shader: " << std::endl;
vShaderF.read(vShaderData, 2000);
for (int i = 0; i < 2000; i++) {
std::cout << vShaderData[i];
}
std::cout << std::endl << std::endl;
std::cout << m << "frag shader: " << std::endl;
fShaderF.read(fShaderData, 2000);
for (int i = 0; i < 2000; i++) {
std::cout << fShaderData[i];
}
std::cout << std::endl;
vShaderF.close();
fShaderF.close();
}
This worked great. my shader file was not actually not 2000 in length, but the read() call seemed to store the extra characters as whitespace into the char array which is what I wanted.
Now having restructured my code a little bit in a newer program, my reader now looks like this:
std::ifstream shaderFile;
shaderFile.open(path);
if (shaderFile.is_open()) cout << "Shader at: " << path << ", initalized" << endl;
char data[2000];
shaderFile.read(data, 2000);
for (int i = 0; i < 2000; i++) std::cout << data[i];
The actual text portion still reads correct. However, now the extra space in the char array is stored with this instead of whitespace:
In case the image won't show, it is basically just a reapeating pattern of these two characters [|[|[|....
Why is this happening and how can I fix it?
NOTE: I'm using the same shader file, same computer, same IDE, same everything. The old one still works.
When using std::istream:read() it will not set the parts of the buffer to spaces which were not read. The memory will be left untouched. If you want to get spaces into an unread area of the buffer, you'll need to put the spaces there yourself. If the program indeed had spaces in the buffer it was because the buffer somehow already contained spaces by chance.
You can use std::istream::gcount() to determine how many characters were read.
If you want the arrays to contain predefined data, you'll have to initialize it with such predefined data. If the stream reads fewer data than the array size, you will have the padding you want.
First off, I know there are posts with similar problems, but I cannot find the solution to mine in any of them.
This is for a programming assignment using binary and text files to store "Corporate sales data." (Division name, quarter, and sales), and then to search for specified records inside the binary data file and display them.
Here are the important parts of my code:
#include stuff
...
// Struct to hold division data
struct DIVISION_DATA_S
{
string divisionName;
int quarter;
double sales;
};
int main()
{
...
// Open the data file
fstream dataFile;
dataFile.open(dataFilePath, ios::in | ios::out | ios::binary);
... Get data from user, store in an instance of my struct ...
// Dump struct into binary file
dataFile.write(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
// Cycle through the targets file and display the record from divisiondata.dat for each entry
while(targetsFile >> targetDivisionName)
{
int targetQuarter; // Target quarter
string targetQuarterStr;
targetsFile.ignore(); // Ignore the residual '\n' from the ">>" read
getline(targetsFile, targetQuarterStr);
targetQuarter = atoi(targetQuarterStr.c_str()); // Parses into an int
cout << "Target: " << targetDivisionName << " " << targetQuarter << endl;
// Linear search the data file for the required name and quarter to find sales amount
double salesOfTarget;
bool isFound = false;
while (!isFound && !dataFile.eof())
{
cout << "Found division data: " << targetDivisionName << " " << targetQuarter << endl;
DIVISION_DATA_S divisionData;
// Read an object from the file, cast as DIVISION_DATA_S
dataFile.read(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
cout << "Successfully read data for " << targetDivisionName << " " << targetQuarter << endl
<< "Name: " << divisionData.divisionName << ", Q: " << divisionData.quarter << ", "
<< "Sales: " << divisionData.sales << endl;
// Test for a match of both fields
if (divisionData.divisionName == targetDivisionName && divisionData.quarter == targetQuarter)
{
isFound = true;
cout << "Match!" << endl;
salesOfTarget = divisionData.sales;
}
}
if (!isFound) // Error message if record is not found in data file
{
cout << "\nError. Could not find record for " << targetDivisionName
<< " division, quarter " << targetQuarter << endl;
}
else
{
// Display the corresponding record
cout << "Division: " << targetDivisionName << ", Quarter: " << targetQuarter
<< "Sales: " << salesOfTarget << endl;
totalSales += salesOfTarget; // Add current sales to the sales accumulator
numberOfSalesFound++; // Increment total number of sales found
}
}
Sorry for the lack of indent for the while loop, copy/paste kind of messed it up.
My problem appears when attempting to access information read from the binary file. For instance, when it tries to execute the cout statement I added for debugging, it gives me this error:
Unhandled exception at 0x0FED70B6 (msvcp140d.dll) in CorporateSalesData.exe: 0xC0000005: Access violation reading location 0x310A0D68.
Now, from what I have read, it seems that this means something is trying to read from the very low regions of memory, AKA something somewhere has something to do with a null pointer, but I can't imagine how that would appear. This whole read operation is copied exactly from my textbook, and I have no idea what a reinterpret_chast is, let alone how it works or how to fix errors with it. Please help?
EDIT: Thanks for all the help. To avoid complications or using something I don't fully understand, I'm gonna go with switching to a c-string for the divisionName.
dataFile.write(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
Works only if you have POD types. It does not work when you have a std::string in there. You'll need to use something along the lines of:
// Write the size of the string.
std::string::size_type size = divisionDat.divisionName.size();
dataFile.write(reinterpret_cast<char*>(&size), sizeof(size));
// Now write the string.
dataFile.write(reinterpret_cast<char*>(divisionDat.divisionName.c_str()), size);
// Write the quarter and the sales.
dataFile.write(reinterpret_cast<char*>(&divisionDat.quarter), sizeof(divisionDat.quarter));
dataFile.write(reinterpret_cast<char*>(&divisionDat.sales), sizeof(divisionDat.sales));
Change the read calls to match the write calls.
// Dump struct into binary file
dataFile.write(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
/*...*/
// Read an object from the file, cast as DIVISION_DATA_S
dataFile.read(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
This will categorically not work under any circumstances.
std::string uses heap-allocated pointers to store any string data it contains. What you're writing to the file is not the contents of the string, but simply the address where the string's data is located (along with some meta-data). If you arbitrarily read those pointers and treat them as memory (like you are in the cout statement) you'll reference deleted memory.
You have two options.
If all you want is a struct that can be easily serialized, then simply convert it like so:
// Struct to hold division data
struct DIVISION_DATA_S
{
char divisionName[500];
int quarter;
double sales;
};
Of course, with this style, you're limited to interacting with the name as a c-string, and also are limited to 500 characters.
The other option is to properly serialize this object.
// Struct to hold division data
struct DIVISION_DATA_S
{
string divisionName;
int quarter;
double sales;
string serialize() const { //Could also have the signature be std::vector<char>, but this will make writing with it easier.
string output;
std::array<char, 8> size_array;
size_t size_of_string = divisionName.size();
for(char & c : size_array) {
c = size_of_string & 0xFF;
size_of_string >>= 8;
}
output.insert(output.end(), size_array.begin(), size_array.end());
output.insert(output.end(), divisionName.begin(), divisionName.end());
int temp_quarter = quarter;
for(char & c : size_array) {
c = temp_quarter & 0xFF;
temp_quarter >>= 8;
}
output.insert(output.end(), size_array.begin(), size_array.begin() + sizeof(int));
size_t temp_sales = reinterpret_cast<size_t>(sales);
for(char & c : size_array) {
c = temp_sales & 0xFF;
temp_sales >>= 8;
}
output.insert(output.end(), size_array.begin(), size_array.end());
return output;
}
size_t unserialize(const string & input) {
size_t size_of_string = 0;
for(int i = 7; i >= 0; i--) {
size_of_string <<= 8;
size_of_string += unsigned char(input[i]);
}
divisionName = input.substr(7, 7 + size_of_string);
quarter = 0;
for(int i = 10 + size_of_string; i >= 7 + size_of_string; i--) {
quarter <<= 8;
quarter += unsigned char(input[i]);
}
size_t temp_sales = 0;
for(int i = 18 + size_of_string; i >= 11 + size_of_string; i--) {
temp_sales <<= 8;
temp_sales += unsigned char(input[i]);
}
sales = reinterpret_cast<double>(temp_sales);
return 8 + size_of_string + 4 + 8;
}
};
Writing to files is pretty easy:
dataFile << divisionData.serialize();
Reading can be a little harder:
stringstream ss;
ss << dataFile.rdbuf();
string file_data = ss.str();
size_t size = divisionData.unserialize(file_data);
file_data = file_data.substr(size);
size = divisionData.unserialize(file_data);
/*...*/
By the way, I haven't checked my code for syntax or completeness. This example is meant to serve as a reference for the kind of code that you'd need to write to properly serialize/unserialize complex objects. I believe it to be correct, but I wouldn't just throw it in untested.
welcome to the world of serialization. You are trying to 'bit blit' your structure into a file. This only works for very simple types (int, float, char[xxx]) where the data is actually inline. And even when it does work you are stuck with reloading the data into the same type of machine (same word size, same endianness).
What you need to do is serilaze the data and then deserialize it back. You can invent ways of doing that yourself or you can use one on many standards. There are 2 basic types - binary (efficient, not human readable) and text (less efficient but human readable)
text
json
yaml
xml
csv
binary
protobuf
boost has a serialization library http://www.boost.org/doc/libs/1_61_0/libs/serialization/doc/
Also you might like to look here
https://isocpp.org/wiki/faq/serialization