C++ ofstream doesn't write expected numbers into file - c++

Good day,
I'm working on a program, which reads in a binary file (functions for read in are given and work) and then write the modified information back in another binary file. (I'm changing between two formats.) If I use std::cout to give out the numbers to the console right before (or after) I use ofstream to write them in a file. I get out the numbers I anticipate, but if I know input them into ofstream via << or write and then look at the contents of the file. (The conversion from this format to the original one exists and I can use it.) Then I get carbage.
struct Record {
unsigned latch;
float e, x, y, u, v, wt;}
I read in the given file and modify the numbers I read in. (The following is part of the main-function). Header h is defined and there exists a default constructor. phsp.read and copy are given and work. Reclen corresponds to the Record length and ensures, that the header is written right, because it can be different for different inputs.
std::ofstream outs;
outs.open(outfile, std::ios::out | std::ios::trunc);
Header h;
outs.write((char*) &h, reclen - 5);
for (int i=0; i<nMax; ++i){
phsp.read(mp);
copy(mp, ep);
Record rec;
Modify numbers and putting them into rec, which is a struct and given above. Rec should be written into the file
outs<<rec.latch<<rec.e<<rec.x<<rec.y<<rec.u<<rec.v<<rec.wt;
outs.write((char*) &rec.latch, 4);outs.write((char*) &rec.e, 4);
outs.write((char*) &rec.x, 4); outs.write((char*) &rec.y, 4);
outs.write((char*) &rec.u, 4);outs.write((char*) &rec.v, 4);
outs.write((char*) &rec.wt, 4);
outs.write((char*) &rec, reclen);
}
Not one of this writes, what I want into the file, but if I give out the numbers through std::cout and the console. I get the expected numbers.
std::cout<<rec.latch<<' '<<rec.e<<' '<<rec.x<<' '<<rec.y<<' '<<rec.u<<' '<<rec.v<<
' '<<rec.wt<<'\n';
I also checked the outs stream with good() and I also checked that the file is open. I can also write into the file. Also the write and the << output don't give the same output (could be expected). I checked the size of the rec.i and it also corresponds to the size of the structs.
At the end I change the header again, because I have now new information.
outs.seekp(5);
outs.write((char*) &h, reclen - 5);
outs.close();
There I also have the same problem, with false numbers. I noticed that a number of 0 corresponds to 0, but the number 1 is converted to 16777216 for a unsigned int number.
I hope you can help me, I have no idea what is wrong.

You need to decide whether your new file format is supposed to use binary or text representation for data.
Typically when you use ofstream::write() method you're saving your data in binary format, which means it will be represented as an array of bytes with the length equal to the data structure you're writing. Therefore what you will find in the file will not be the number but its binary representation which generally looks like rubbish if seen as text.
You may see the number by using a hex editor, or you can read it back and then interpret it as a type of your choice. For example:
float number;
ifs.read(&number, sizeof(float); // read in the 4 bytes from the file into a float
This is an equivalent of doing:
char* buffer = new char[sizeof(float)];
ifs.read(buffer, sizeof(float)];
float f = *(reinterpret_cast<float*>(buffer));
If you printed out the contents of the buffer before casting it, you'd see the same rubbish you see in your file. Note that reading and writing binary data requires ios::binary flag set.
operator<< is typically used for writing data as text. It is less efficient in terms of space and speed, but has an advantage of being human readable. Writing data as text is as simple as printing it to the screen, e.g.
std::ofstream ofs;
ofs.open("file.txt");
float f = 1.337f;
ofs << "My float: " << f;
std::cout << "My float: " << f;
If you use above code in the same scope, there is no way for the contents in your file to be different from what's shown on your screen.
Finally make sure that your variables are initialized before using them, as in the code you've posted you're not doing it. E.g.
Header h; // <-- this probably contains rubbish until initialized with some values
outs.write((char*) &h, reclen - 5); // <-- writes rubbish

Related

How to increse numbers in file?

I need to read file in parts ( for example by 4 bytes) and then increment numbers in files by one and then write back;
this part only fills in file by 1; How to increase this number on 1?
void Prepare()
{
//ifstream fileRead("\FILE", ios::in | ios::binary);
ofstream fileOut("\FILE.bin", ios::out | ios::binary);
int count = 10485760;
for (int i = 0; i < count-1; i++)
{
fileOut << 1;
}
fileOut.close();
}
If I understand your question, you need to read the file then write it out, changing the data. You can't really do it the way you've started.
There are two basic ways to do this. You can read the entire file into memory, then manipulate the memory, close the file, open it again for output this time (truncating it) and write it back out. This is easiest, but I don't think it's the approach you're looking for.
The other choice is to manipulate the file in place. That's trickier, but not that hard. You need to read about random access I/O (input/output). If you google for c++ random access file you'll get some good hits, but I'll show you a little bit.
// Open the file.
std::ifstream file{"file.dat"};
// Jump to a particular location in the file. Beginning is 0.
file.seekg(128);
// Read 4 bytes
char bytes[4];
file.read(bytes, 4);
// Manipulate it (more below)
int number = bytesToInt(bytes);
++number;
intToBytes(number, bytes);
// Seek again
file.seekg(128);
file.write(bytes, 4);
So the only remaining trick is that you have to convert the bytes to a number and then back into bytes. Due to endianness, it's not safe to read directly into the number. You also need to know the endianness of the data in the file. That's a separate topic you can look up if you're not already familiar with it.
(Specifically, you need to implement those two methods after verifying how the data is stored in your file.)
There may be other ways to do this, but the key to this method is the random access file.

Binary file not holding data properly

Im currently trying to replace a text based file in my application with a binary one. Im just doing some early tests so the code isn't exactly safe but I'm having problems with the data.
When trying to read out the data it gets about half way before it starts coming back with incorrect results.
Im creating the file in c++ and my client application is c#. I think the problem is in my c++ (which I haven't used very much)
Where the problem is at the moment is I have a vector of a struct that is called DoubleVector3 which consists of 3 doubles
struct DoubleVector3 {
double x, y, z;
DoubleVector3(std::string line);
};
Im currently writing the variables individually to the file
void ObjElement::WriteToFile(std::string file) {
std::ofstream fileStream;
fileStream.open(file); //, ios::out | ios::binary);
// ^^problem was this line. it should be
// fileStream.open(file, std::ios_base::out | std::ios_base::binary);
fileStream << this->name << '\0';
fileStream << this->materialName << '\0';
int size = this->vertices.size();
fileStream.write((char*)&size,sizeof(size));
//i have another int written here
for (int i=0; i<this->vertices.size(); i++) {
fileStream.write((char*)&this->vertices[i].x, 8);
fileStream.write((char*)&this->vertices[i].y, 8);
fileStream.write((char*)&this->vertices[i].z, 8);
}
fileStream.close();
}
When I read the file in c# the first 6 sets of 3 doubles are all correct but then I start getting 0s and minus infinities
Am I doing anything obviously wrong in my WriteToFile code?
I have the file uploaded on mega if anyone needs to look at it
https://mega.co.nz/#!XEpHTSYR!87ihtCfnGXJJNn13iE6GIpeRhlhbabQHFfN88kr_BAk
(im writing the name and material in first then the number of vertices before the actual list of vertices)
Small side question - Should I delimit these doubles or just add them in one after the other?
To store binary data in a stream, you must add std::ios_base::binary to the stream's flags when opening it. Without this, the stream is opened in text mode and line-ending conversions can happen.
On Windows, line-ending conversions mean inserting a byte 0x0D (ASCII for carriage-return) before each 0x0A byte (ASCII for line-feed). Needless to say, this corrupts binary data.

Converting between text files and binary files in C++

For converting an ordinary text file into binary and then convert that binary file back to a text file so that the first text file equals with the last text file, I have wrote below code.
But the bintex text file and the final text file aren't equal. I don't know which part of code is incorrect.
Input sample ("bintex") contains this: 1983 1362
The result ("final") contains this: 959788084
which of course are not equal.
#include <iostream>
#include <fstream>
using namespace std;
int main() try
{
string name1 = "bintex", name2 = "texbin", name3 = "final";
ifstream ifs1(name1.c_str());
if(!ifs1) error("Can't open file for reading.");
vector<int>v1, v2;
int i;
while(ifs1.read(as_bytes(i), sizeof(int)));
v1.push_back(i);
ifs1.close();
ofstream ofs1(name2.c_str(), ios::binary);
if(!ofs1) error("Can't open file for writting.");
for(int i=0; i<v1.size(); i++)
ofs1 << v1[i];
ofs1.close();
ifstream ifs2(name2.c_str(), ios::binary);
if(!ifs2) error("Can't open file for reading.");
while(ifs2.read(as_bytes(i), sizeof(int)));
v2.push_back(i);
ifs2.close();
ofstream ofs2(name3.c_str());
if(!ofs2) error("Can't open file for writting.");
for(int i=0; i<v2.size(); i++)
ofs2 << v2[i];
ofs2.close();
keep_window_open();
return 0;
}
//********************************
catch(exception& e)
{
cerr << e.what() << endl;
keep_window_open();
return 0;
}
What is this?
while(ifs1.read(as_bytes(i), sizeof(int)));
It looks like a loop that reads all input and throws it away. The line afterward suggests that you should be using braces instead of a semicolon there, and doing the write in the block.
Your read and write operations aren't symmetric.
ifs1.read(as_bytes(i), sizeof(int))
grabs 4 bytes, and dumps the values into the char* its passed.
ofs1 << v1[i];
output the integer in v[i] as text. Those are very very different formats.
If you used >> to read you would have a lot more success.
To expound, the first read might look like this {'1','9','8','3'}, which I would guess would be the 959788084 you are seeing when you pun it to an int. Your second read would be {' ','1','3','6'}, like not what you'd hoped for either.
It's not clear (to me, at least), what you are trying to do.
When you say that the orginal file contains 1983 1262, what do
you really mean? That it contains two four byte integers, in
some unspecified format, whose values are 1983 and 1262? If so,
the problem is probably due to your machine not using the same
format. You cannot, in general, just read bytes (using
istream::read) and expect them to mean anything in your
machine's internal format. You have to read the bytes into
a buffer, and unformat them, according to the format with which
they were written.
Of course, opening a stream in binary mode doesn't mean that
the actual data are in some binary format; it just affects
things like how (or more strictly speaking, whether) line
endings are encoded, and how end of file is recognized.
(Strictly speaking, a binary file is not divided into lines. It
is just a sequence of bytes. Of course, some of those bytes
might have values that you, in your program, interpret and new
line characters.) If your file actually contains nine bytes
with characters corresponding to "1983 1362", then you'll have
to parse them as a text format, even if the file is written in
binary. You can do this by reading the entire file into
a string, and usingstd::istringstream; _or_, on most common
systems (but not necessarily on all exotics) by using>>` to
read, just as you would with a text file.
EDIT:
Just a simple reminder: you don't show the code for as_bytes,
but I'm willing to guess that there's a reinterpret_cast in
it. And any time you have to use a reinterpret cast, you can be
very sure that what you're doing isn't portable, and if it's
supposed to be portable, you're doing it wrong.

C++: Reading and Sorting Binary Files

I've been scratching my head and putting this homework off for a couple days but now that I hunker down to try and do it I'm coming up empty. There's 4 things I need to do.
1) Read a binary file and place that data into arrays
2) Sort the list according to the test scores from lowest to highest
3) Average the scores and output it
4) Create a new binary file with the sorted data
This is what the binary data file looks unsorted
A. Smith 89
T. Phillip 95
S. Long 76
I can probably sort since I think I know how to use parallel arrays and index sorting to figure it out, but the reading of the binary file and placing that data into an array is confusing as hell to me as my book doesn't really explain very well.
So far this is my preliminary code which doesn't really do much:
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <Windows.h>
using namespace std;
int get_int(int default_value);
int average(int x, int y, int z);
int main()
{
char filename[MAX_PATH + 1];
int n = 0;
char name[3];
int grade[3];
int recsize = sizeof(name) + sizeof(int);
cout << "Enter directory and file name of the binary file you want to open: ";
cin.getline(filename, MAX_PATH);
// Open file for binary write.
fstream fbin(filename, ios::binary | ios::in);
if (!fbin) {
cout << "Could not open " << filename << endl;
system("PAUSE");
return -1;
}
}
Sorry for such a novice question.
edit: Sorry what the data file stated earlier is what it SHOULD look like, the binary file is a .dat that has this in it when opened with notepad:
A.Smith ÌÌÌÌÌÌÌÌÌÌÌY T. Phillip ÌÌÌÌÌÌÌÌ_ S. Long ip ÌÌÌÌÌÌÌÌL J. White p ÌÌÌÌÌÌÌÌd
Reading a file in c++ is simple:
create a stream from file [so that to read from the stream] (you have filestream[input/output], stringstream ... )
ifstream fin; //creates a fileinput stream
fin.open(fname.c_str(),ifstream::binary); // this opens the file in binary mod
void readFile(string fname)
{
ifstream fin;
fin.open(fname.c_str()); //opens that file;
if(!fin)
cout<<"err";
string line;
while(getline(fin,line)) //gets a line from stream and put it in line (string)
{
cout<<line<<endl;
//reading every line
//process for you need.
...
}
fin.close();
}
as you specify, the file is simply a text file, so you can process each line and do whatever you want.
Reading from a binary file may seem confusing, but it is really relatively simple. You have declared your fstream using your file name and set it to binary, which leaves little to do.
Create a pointer to a character array (typically called a buffer, since this data is typically extracted from this array after for other purposes). The size of the array is determined by the length of the file, which you can get by using:
fbin.seekg(0, fbin.end); //Tells fbin to seek to 0 entries from the end of the stream
int binaryLength = fbin.tellg(); //The position of the stream (i.e. its length) is stored in binaryLength
fbin.seekg(0, fbin.beg); //Returns fbin to the beginning of the stream
Then this is used to create a simple character array pointer:
char* buffer = new char[binaryLength];
The data is then read into the buffer:
fbin.read(buffer, binaryLength);
All the binary data that was in the file is now in the buffer. This data can be accessed very simply as in a normal array, and can be used for whatever you please.
The data you have, however, does not at all seem binary. It looks more like a regular text file. Perhaps, unless explicitly stated, you ought to consider a different method for reading your data.
You know, with that low range of sorting index you can avoid actual sorting (with comparing indices and moving data forth and back). All you have to do is to allocate a vector of vector of strings, resize it to 101. Then traverse the data, storing each: "A. Smith" in 89-th element; "T. Phillip" in 95-th; "S. Long" in 76-th and so on.
Then by iterating the vector elements from begin() to end() you would have all the data already sorted.
It's almost linear complexity (almost, because allocation/resizing of subvectors and strings can be costly) easy and transparent.

Problem with iostream,first write file in Binary mode then convert to and save ASCII

I am using iostream to collect data from a device.First I Write file in ASCII mode,it works well,but taking quite alot of time.Then I switsch to write in Binary mode and it is faster.
float dist[41616];
ofstream d_ofs( "numbers.dat",ios::out |ios::app|ios::binary);
// this is from the device API getting distance datas
res = GetDistances (hnd, dist, sizeof dist);
d_ofs.write((char*)&dist,sizeof dist);
d_ofs.close();
After the data collection,I have to convert the data inside "numbers.txt" to be readable by my tutor, in a txt file.I assume it is to change binary raw data to ASCII. so I read the file first and then write in ASCII format.
I get numbers,but unfortunately they are not the right numbers.I guess there should be special format for this convert process.But so far I have not found the solution due to my poor coding knowledge.So can anyone give me a hand? Many thanks in advance.
Here is my code:
double fnum[41616]; // here is the mistake,it should be float[]
ifstream in("numbers.dat", ios::in | ios::binary);
in.read((char *) &fnum, sizeof fnum);
MessageBox("Read File done");
ofstream fout("output.txt");
for(int k=0; k<41616; k++) // show values read from file
fout <<dec<<"\t" << fnum[k] ;
in.close();
fout.close();
Conclude:
The stupid mistake by me is in the data type.I wrote double[] instead of float[] in the second part.It is really embarrasing.Hopefully this post can help beginners like me.
You seem to be saving as float and reading as double. The read will (usually) be twice as large as the write so every double you read will actually be reading two of the written floats. You will get the wrong numbers because of this.
try changing the
double fnum[41616]
to
float fnum[41616]
Also, its probably for the best not to call a binary file *.txt as its a little confusing.
Its also confusing calling a double array fnum - as the f suggests floats