how can i write an array of structures to a binary file and read it again? - c++

I'm writing an array af structures(factor is a structure) to a binary file like this:
factor factors[100];
ofstream fa("Desktop:\\fa.dat", ios::out | ios::binary);
fa.write(reinterpret_cast<const char*>(&factors),sizeof(factors));
fa.close();
and I run the program and save 5 records in it.in another file, I want to read the structures so I wrote this:
int i=0;
ifstream a("Desktop:\\fa.dat", ios::in | ios::binary);
factor undelivered_Factors[100];
while(a && !a.eof()){
a.read(reinterpret_cast<char*>(&undelivered_Factors),sizeof(undelivered_Factors));
cout<<undelivered_Factors[i].ID<<"\t"<<undelivered_Factors[i].total_price<<endl;
i++;
}
a.close();
but after reading and printing the saved factors it inly reads and shows the firs 2 of them in the array.why?what should i do?

Second parameter of ofstream::write and ::read is size of written memory in bytes (aka 'char' in C\C++), which is right - you're writing entire array at once. In reading procedure you had mixed up an per element and array processing. You expect to read whole array, then you print one value, then you read another 100 of records which you do not have in file, I presume. also eof() happens only when you attempt to read and it failed. If you stand on end of file,eof() isn't triggered, that's why you get two records printed.

You are doing complete read in the single call so your loop runs only one time hence it will output only first struct value. Change your while loop like this:
if(a)
{
a.read(reinterpret_cast<char*>(&undelivered_Factors),sizeof(undelivered_Factors));
}
for(int i=0; i<100; ++i)
{
cout<<undelivered_Factors[i].ID<<"\t"<<undelivered_Factors[i].total_price<<endl;
}

Related

C++ ofStream: "<<" vs "put"

Being new to C++, I am confused about what << and put() means while using ofstream to write to a text file. I tried to experiment with the two following styles as follows:
Approach 1:
void writeTester() {
std::ofstream oFile("Resources/tst.txt", std::ios::out | std::ios::trunc);
std::vector<int> v{ 1,2,3,4 };
for (int i = 0;i < 4;i++) {
oFile.put(v[i]);
//casting to character pointer and writing also produced similar result
//oFile.put(*(char*)&v[i]);
}
oFile.close();
}
Approach 2:
void writeTester() {
std::ofstream oFile("Resources/tst.txt", std::ios::out | std::ios::trunc);
std::vector<int> v{ 1,2,3,4 };
for (int i = 0;i < 4;i++) {
oFile << v[i];
}
oFile.close();
}
While Approach 2 wrote the expected result to file (1234), Approach 1 wrote some garbage value to the file.
What is the difference between the 2 styles, and when to use which one? Also, what is the correct way to use Approach 1 to have "1234" as the output written to the file?
Simply put, using the '<<' operator means certain overloads can be used, which means writing integer values (as in your case) will mean they're converting to their string representations before being written to the file.
Using put, however, will write a single byte to the stream. This means your 4 byte int will be truncated to a single byte, thus writing nonsense data to your file.
Here's the documentation for ofstream::put.
And here's the documentation for ofstream.
For completion, and to spark your interest, here's an ASCII table, where you can look up the values you were writing to the file. Depends on the byte order (endianness) of your machine, which char is written. On LE machines it should be NUL, but I get confused myself sometimes and mix up the byte orders in my head so please take that last sentence with a grain of salt.

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.

Merging two text files gives wierd results

I need to merge two text files by putting them in a vector array and then writing them in a new text file.
After merging them.The new file has extra characters.
FE:
f1.txt ("text1")
f2.txt ("text2.")
f12.txt ("text1˙text2.˙W64")
Content of the buffer: "text1 text2. W64"
Here is the code:
int main(){
enum errorcode{FNF,FNC};
vector<char> buffer;
char ime[255];
cin>>ime;//first file
ifstream ud1(ime,ios::in);
if(ud1.is_open()){
while(!ud1.eof())buffer.push_back(ud1.get());
ud1.close();
}
else {cout<<"File not found.";return FNF;}
cin>>ime;//second file
ifstream ud2(ime,ios::in);
if(ud2.is_open()){
while(!ud2.eof())buffer.push_back(ud2.get());
ud2.close();
}
else {cout<<"File not found.";return FNF;}
cin>>ime;//new file
ofstream id(ime,ios::out);
if(id.is_open()){
for(int i=0;i<buffer.capacity();i++)id.put(buffer[i]);
id.close();
}
else {cout<<"File not created.";return FNC;}
return 0;
}
I guess this is because of notepad or files themselves.
Can you please tell me reason for this.
you are using Vector capacity: Returns the size of the storage space currently allocated for the vector, expressed in terms of elements.
You must use vector size: Returns the number of elements in the vector. This is the number of actual objects held in the vector, which is not necessarily equal to its storage capacity.
About the ˙
please look at istream::get return value:
Return Value
The first signature returns the character read, or the end-of-file value (EOF) if no characters are available in the stream (note that in this case, the failbit flag is also set).
So, you could change the loop to this:
while(!ud1.eof()){
int tmpChar = ud1.get();
if( !ud1.eof() )
buffer.push_back(tmpChar);
}

does fstream move to the next position after read in a binary integer (c++)

I am trying to read in a binary file and write in chunks to multiple output files. Say if there are 25 4byte numbers in total and chunk size is set to 20, the program will generate two output files one with 20 integers the other with 5. However if I have a input file with 40 integers, my program generates three files, first 2 files both have 20 numbers, however the third file has one number which is the last one from the input file and it is already included in the second output file. How do I force the read position to move forward every time reading a number?
ifstream fin("in.txt", ios::in | ios::binary);
if(fin.is_open())
{
while(!fin.eof()){
//set file name for each output file
fname[0] = 0;
strcpy(fname, "binary_chunk");
index[0] = 0;
sprintf(index, "%d", fcount);
strcat(fname, index);
// open output file to write
fout.open(fname);
for(i = 0; i < chunk; i++)
{
fin.read((char *)(&num), INT_SIZE);
fout << num << "\n";
if(fin.eof())
{
fout.close();
fin.close();
return;
}
}
fcount ++;
fout.close();
}
fout.close();
}
The problem is most likely your use of while (!fin.eof()). The eofbit flag is not set until after you have tried to read from beyond the end of the file. This means that the loop will loop one extra time without you noticing.
Instead you should remember that all input operations returns the stream object, and that stream objects can be used as boolean conditions. That means you can do like this:
while (fin.read(...))
This is safe from the problems with looping while !fin.eof().
And to answer your question in the title: Yes, the file position is moved when you successfully read anything. If you read X bytes, the read-position will be moved forward X bytes as well.

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.