C++ file stream for reading/writing - c++

I need to open a file for both reading/writing using fstream and read each character then write that character back to the file. for example i have this code.
fstream in("test.txt",ios::in | ios::out);
if(!in)
cout<<"error...";
else
{
char ch;
in.seekg(0,ios::end);
int end=in.tellg();//get the length
in.seekg(0);//get back to the start
for(int i=0;i<end;i++)
{
//in.seekg(in.tellg());//if i uncomment this the code will work
if(!in.get(ch).fail())//read a character
{
in.seekp(static_cast<int>(in.tellg())-1);//move the pointer back to the previously read position,so i could write on it
if(in.put(ch).fail())//write back,this also move position to the next character to be read/write
break;//break on error
}
}
}
I have a file named "test.txt" which contains "ABCD". As i understand it both put() and get() methods of the stream object move the file pointer forward(i see that by getting the return value of tellg() or tellp() functions after each get() or put() method call). My question is when i comment out the code that will seek the stream pointer to "where it is now"(in.seekg(in.tellg()), the code will result incorrect results. I don't understand why this is since tellg() is showing the correct position of the character to be read next.what is the purpose of explicitly seeking to it? I am using visual studio 2005.
The incorrect result is it writes to the file "ABBB" instead of "ABCD".

The output buffer has to be flushed when switching between write and read.
fstream in("test.txt",ios::in | ios::out);
if(!in)
cout<<"error...";
else
{
char ch;
in.seekg(0,ios::end);
int end=in.tellg();//get the length
in.seekg(0);//get back to the start
for(int i=0;i<end;i++)
{
if(!in.get(ch).fail())//read a character
{
in.seekp(static_cast<int>(in.tellg())-1);//move the pointer back to the previously read position,so i could write on it
if(in.put(ch).fail())//write back,this also move position to the next character to be read/write
break;//break on error
in.flush();
}
}
}

Related

how to make a new line in binary file c++?

Can anybody help me with this simple thing in file handling?
This is my code:
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
int main()
{
fstream f;
f.open("input05.bin", ios_base::binary | ios_base::out);
string str = "";
cout << "Input text:"<<endl;
while (1)
{
getline(cin, str);
if (str == "end")
break;
else {
f.write((char*)&str, sizeof(str));
}
}
f.close();
f.open("input05.bin", ios_base::in | ios_base::binary);
while (!f.eof())
{
string st;
f.read((char*)&st, sizeof(st));
cout << st << endl;
}
f.close();
}
It is running successfully now. I want to format the output of the text file according to my way.
I have:
hi this is first program i writer this is an experiment
How can I make my output file look like the following:
hi this is first program
I writer this is an experiment
What should I do to format the output in that way?
First of all,
string str;
....
f.write((char*)&str, sizeof(str));
is absolutely wrong as you cast a pointer to an object of type std::string to a pointer to a character, i.e. char*. Note that an std::string is an object having data members like the length of the string and a pointer to the memory where the string content is kept, but it is not a c-string of type char *. Further, sizeof(str) gives you the size of the "wrapper object" with the length member and the pointer, but it does not give you the length of the string.
So it should be something like this:
f.write(str.c_str(), str.length());
Another thing is the os-dependant handling of new line character. Depending on the operating system, a new line is represented either by 0x0d 0x0a or just by 0x0d. In memory, c++ treats a new line always as a single character '\n'(i.e. 0x0d). When writing to a file in text mode, c++ will expand an '\n' to 0x0d 0x0a or just keep it as 0x0d (depending on the platform). If you write to a file in binary mode, however, this replacement will not occur. So if you create a file in binary mode and insert only a 0x0d, then - depending on the platform - printing the file in the console will not result in a new line.
Try to write ...
f.write(str.c_str(), str.length());
f.put('\r');
such that it will work on your platform (and will not work on other platforms then).
That's why you should write in text mode if you want to write text.

File handling not working with fstream after reaching eof?

On your suggestion i have changed the code as you suggested but still problems are there when ios::out is replaced with ios::ate nothing is written in the file(Writing does not work). Is there any way to check that if the next bit is eof rather than reading it and then checking it? as suggested by you.And sometimes when i do file handling it shows the position of file pointer to be -1 what could that mean???
Code:
int main ()
{
char p[80];
fstream file("text1.txt",ios::out|ios::in); //if ios::ate is added here it results into infinite loop
cout<<"Starting position of the file is "<<file.tellg()<<endl;
getch();
if(file.is_open())
cout<<"file is open\n";
else
cout<<"file is not open\n";
getch();
file.seekp(0);
while(file>>p)
{
cout<<p<<endl;
}
file.clear();
cout<<"\nThe current position of the file pointer is "<<file.tellg()<<endl;
file.seekp(0);
if(file.eof())
cout<<"\n the eof\n";
while(file>>p)
{
cout<<p<<endl;
}
file.close();
return 0;
}
Output:
Starting position of the file is 0
file is open
Hello
man
how
are
you
The current position of the file pointer is 21
Hello
man
how
are
you
With this kind of reading from file reaching end-of-file causes setting both eof and failbit. Failbit is setted because creating your read loop with file.eof() condition doesn't indicate that next read will be the end of the stream. It just states that we didn't reach eof yet, so with:
while(file.eof())
{
file >> p;
}
It's possible that last read will be eof only, and we'll work with uninitialised data. IF this happens no characters will be extracted inside p and both eof and fail flags will be set.
When working with c++98 need to reset failbit to false by using:
file.clear();
To avoid bad readings situation you should extract characters from file inside while condition: while(file >> p). I recommend this or this questions on stack overflow.
So proper C++98 code should look like this:
while(file >> p)
{
std::count << p << std::endl;
}
file.clear();
file.seekp(0);
while(file >> p)
{
std::count << p << std::endl;
}
file.close();
I tested it couple of times on Visual Studio 2013 and it worked everytime.
Considering ios::ate mode:
ios::out, ios::in are modifiers that states how do we open file in question. If you want to read something from file you need to use ios::out flag, and for writing you need to use ios::in.
On the other hand ios::ate just tells compiler to open file and immediately go to the end of file. So if you substitute ios::out with ios::ate writing would be impossible, and program will rise failflag on file << "Hello...";.
And if you just want to append data, but read from the beginning of file you should use ios::app instead, because it tells to seek eof before each write.

c++ this to write object into binary file

I've some trouble with this pointer in c++ function.
I would write my class into binary file, so I write this function member
void Product::saveProducts(){
fstream file;
Product temp;
temp.setId(-1);
bool flag = false;
file.open("cigarettes.dat", ios::out | ios::binary);
if(file.is_open()){
file.seekg(0, ios::end);
if(file.tellg()!=0){
file.seekg(0, ios::beg);
while(!file.eof()){
file.read(reinterpret_cast<char *>(&temp), sizeof(temp));
if(temp.getId() == this->getId()){
flag=true;
file.seekp(-sizeof(temp),ios::cur);
file.write(reinterpret_cast<char *>(this), sizeof(temp));
break;
}
}
}
if(!flag){
file.write(reinterpret_cast<char *>(this), sizeof(temp));
}
}
file.flush();
file.close();
}
But when I try to retrieve my stored object with another function member:
list<Product> Product::loadProducsts(){
fstream file;
Product temp;
list<Product> products;
file.open("cigarettes.dat", ios::in | ios::binary);
if(file.is_open()){
while(!file.eof()){
file.read(reinterpret_cast<char *>(&temp), sizeof(temp));
products.push_front(temp);
}
}
file.close();
return products;
}
My array is filled with only one object empty. What's the problem?
There are any number of problems with your code, starting with
the fact that just dumping bits to a file generally will not
give you anything useful that you can read back. The fact that
you need a reinterpret_cast to use it should have tipped you
off. In addition:
You only open the file for output, and then try to read from
it. (Opening a file for output truncates it, so you've already
lost all of your previous data.)
I'm not sure what you think you're doing with while (!file.eof()),
but it's surely not correct. If for some reason, the
file.read fails without hitting the end of file, you'll end up
in an endless loop, for example.
And you're using the results of file.read without verifying
that the function worked.
You close the file without being at the end. This truncates
a file open in output mode.
Same problems in the read loop of the second snippet. If the
file is empty, you'll still "read" one object, pushing the
default constructed temp into products.
Not knowing what Product looks like, nor the initial contents
of the file, it's hard to say what is really happening. But
most likely:
You truncate the file with the open. It now has a length of
0.
Since file.tellg() returns 0, you never even try to read
it. (If you'd tried to read it, an error would have been set,
which would have made all successive operations no-ops.)
You then write the single element, and close the file.
The only thing I'm not too sure of: in this scenario, the file
actually does contain one element. So when you try to read it,
the first file.read succeeds, probably without setting
eofbit, since file.read doesn't need any look ahead. And if
eofbit isn't set, I would expect you to loop a second time,
and push the unmodified bits in temp into products a second
time.
EDIT:
FWIW: if we assume that you're in the very restricted case
where just writing the data bits to the disk is valid (which
normally means that you'll be rereading them later in the same
process, but never from a different processs), and that the
id can never be -1 in a valid Product, what you probably
want to do is:
Product temp;
temp.setId( -1 ); // This sort of thing should really be handled by a constructor
std::fstream file( "cigartettes.dat", ios::out | ios::in | ios::binary );
while ( file.read( reinterpret_cast<char*>( &temp ), sizeof(temp) && temp.getId() != getId() ) {
}
if ( file.gcount() != 0 ) {
// Error somewhere, we ended up reading a partial record
} else if ( temp.getId() == getId() ) {
file.seekp( -static_cast<int>( sizeof(temp) ) );
} else {
file.clear();
file.flush();
}
file.write( reinterpret_cast<char const*>( this ), sizeof(*this) );
file.close();
if ( !file ) {
// Something went wrong somewhere...
}
Several comments:
Opening in both input and output is necessary. Opening only
in output means that 1) the file will be truncated, and 2) any
attempt to read it will fail.
file.read will fail if it cannot read the correct number of
bytes. If it fails, it might have read some bytes anyway (and
overwritten the id field in Product, and left the current
pointer at some position which isn't a modulo of your object
size). So you should check for this using the value from
file.gcount() (which returns the number of bytes read by the
last unformatted read operation—in the case of the read
you're doing, this can only be different from sizeof(Product)
if the read failed.
When specifying a negative value to seek backwards: you have
to convert the results of sizeof to a signed type before doing
the -. Otherwise, you'll end up with some astronomical
positive value, which will cause you to try to seek beyond the
end of file (which will fail).
When the read fails, and the number of bytes read is 0, you've
reached the end of file. And set the failbit, which will
cause all future operations to fail. So we have to clear the
error if we're going to write to extend the data. (If we
haven't reached end of file, of course, there's nothing to
clear.)
When doing bidirectional input, after a read, you must execute
either a seek or a flush before a write. (Don't ask me why;
it's just what the standard says.)
Finally, it's good practice to verify the status of the file
after closing, when all buffers have been fully flushed and
passed to the OS. If for some reason, a write has failed
somewhere, you want to know about it, to inform the user that
the file that was output is corrupt.
I might add that the usual way of modifying just one record in
a file is to copy the file to a new file, replacing or appending
the changed record, and then delete the old file and rename the
new. Trying to modify a file, as you are doing, can mean that
you loose all of the data if something goes wrong.

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.

need help converting c to c++ (simple error but cant fix)

I have a c++ homework. The homework is asking to convert a c program to c++.
Below is the question:
You are requested to convert the following C function into a C++
function and then embed it into a complete program and test it. Note
that this function copies a binary file of integers and not a text
file. The program must accept the arguments (the file to copy and the
file to be copied to) from the command line.
/* ==================== cpyFile =====================
This function copies the contents of a binary file
of integers to a second file.
Pre fp1 is file pointer to open read file
fp2 is file pointer to open write file
Post file copied
Return 1 is successful or zero if error
*/
int cpyFile (FILE *fp1, FILE *fp2)
{
/* Local Definitions */
int data;
/* Statements */
fseek (fp1, 0, SEEK_END);
if (!ftell (fp1))
{
printf ("\n\acpyFile Error : file empty\n\n");
return 0;
} /* if open error */
if (fseek (fp1, 0, SEEK_SET))
return 0;
if (fseek (fp2, 0, SEEK_SET))
return 0;
while (fread (&data, sizeof (int), 1, fp1))
fwrite (&data, sizeof (int), 1, fp2);
return 1;
} /* cpyFile */
I did my best and managed to convert it, but unfortunately when I'm using it , the file that I get after the copy is empty. Below is my answer:
#include <fstream>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
if(argc!=3)
{cerr<<"invalid number of arguments. must be 3."<<endl;exit(1);}
fstream fp1(argv[1],ios::in);
if(!fp1)+{cerr<<argv[1]<<" could not be opened"<<endl;exit(1);}
fstream fp2(argv[2],ios::out);
if(!fp2)+{cerr<<"file could not be found."<<endl;exit(1);}
int data;
fp1.seekg (0,ios::end);
if (!fp1.tellg ())
{
cout<<"\n\acpyFile Error : file empty\n\n";
return 0;
} /* if open error */
if (fp1.seekg (0, ios::beg))
return 0;
if (fp2.seekg (0, ios::beg))
return 0;
while (fp1.read (reinterpret_cast<char*>(&data), sizeof (int)))
{
fp2.seekp(0);
fp2.write (reinterpret_cast<char*>(&data), sizeof (int));
}
return 1;
}
I did my best and everything is working fine, except that when I copy a binary file, the file that i get is empty and I have no idea why.
You need to open the file in binary mode, as others have said, by doing
fstream fp1(argv[1], ios::in | ios::binary); // combine ios::in with ios::binary
fstream fp2(argv[2], ios::out | ios::binary); // combine ios::out with ios::binary
Or you can make them ifstream (in file stream for reading only) and ofstream (out file stream, for writing only) and remove the ios::in and ios::out because ifstream implies ios::in and ofstream implies ios::out:
ifstream fp1(argv[1], ios::binary);
ofstream fp2(argv[2], ios::binary);
You need to do this because if you don't, the file will be translated when you read from or write to it for things like turning line endings from \r\n or \r to just \n, etc, which will mess up your binary data which may happen to have those bytes in them.
This:
if (fp1.seekg (0, ios::beg))
return 0;
if (fp2.seekg (0, ios::beg))
return 0;
Will always make your code return because seekg returns the object you call it on. It's not the equivalent of fseek in this regard because fseek returns 0 on success. So you never get to the while loop. Take those out of the if statements so that it looks like this:
fp1.seekg(0, ios::beg);
fp2.seekg(0, ios::beg);
Or if you have to have the checking, you want to do
if (!fp1.seekg (0, ios::beg)) // notice the added !
return 0;
if (!fp2.seekg (0, ios::beg)) // notice the added !
return 0;
Also, this (inside the while):
fp2.seekp(0);
Is setting the point you are going to write to to the beginning of the file. So you'll never write anything but at the beginning of the file. Just remove that line completely.
Also, you have a return inside the loop which makes it return on the first iteration. Move the return 1; outside the loop so you only return after the loop is finished. Nevermind that, misread due to the unusual brace style.
Every time you read a new data block from fp1, you rewind fp2 to the beginning of the stream, essentially discarding what you have already written to fp2. Try moving fp2.seekp(0) out of your main loop.
You have a few problems. I'd start by fixing this bit:
if (fp1.seekg (0, ios::beg))
return 0;
if (fp2.seekg (0, ios::beg))
return 0;
The seekg method returns a reference to the istream it's called on, so the above is equivalent to this:
fp1.seekg (0, ios::beg);
if (fp1) // i.e., if fp1 is in a valid state (as opposed to e.g. end-of-file)
return 0;
fp2.seekg (0, ios::beg);
if (fp2) // i.e., if fp2 is in a valid state (as opposed to e.g. end-of-file)
return 0;
which is obviously not what you want.
To debug your code, you can use statements like std::cout << "Got to line " << __LINE__ << std::endl; to figure out which parts of the program are actually being run. That would have found the above problem pretty quickly.
Binary files need to be opened specifically in binary mode, so where you have fstream fp1(argv[1],ios::in); you should also add an ios::binary to it like so: fstream fp1(argv[1], ios::in | ios::binary);
In the C++ code you are seeking to the beginning of the output file before writing each number, and therefore the output file will be at most 2 bytes long.