seekp before writing? - c++

I want to read and write an binary file with a single file stream. The following code tries to read the first part of the file, and uses it to overwrite the second part of the file. But I find that I have to use "seekp(pos [,ios_base::begin]);" before writing. Additionally, "seekp" in fact doesn't change the position in my code, but it is necessary! Could any one give an explain? It should better be according to the c++ standard. Thanks very much!
#include <iostream>
#include <fstream>
using namespace std;
int main(){
fstream flib ("tmp.txt", ios::in | ios::out |ios::binary | ios::trunc);
if(!flib){
cerr << "file open failed!" << endl;
return 1;
}
int tmp;
for(int i = 0; i<2 ; i++){//write 2 numbers
flib.write((char*)&i, sizeof(tmp));
}
flib.seekg(0);
while(flib.read((char*)&tmp, sizeof(tmp))){//read file contents
cout <<tmp<<endl;
}
flib.clear();
flib.seekg(0);
flib.read((char*)&tmp, sizeof(tmp));
flib.seekp(sizeof(tmp)); //work
//flib.seekp(sizeof(tmp), ios_base::beg); //work
//flib.seekp(0, ios_base::cur); //not work
//flib.seekp(sizeof(tmp), ios_base::end); //not work
//flib.seekp(-sizeof(tmp), ios_base::end); //not work
flib.write((char*)&tmp, sizeof(tmp));
flib.clear();
flib.seekg(0);
while(flib.read((char*)&tmp, sizeof(tmp))){//read file contents
cout <<tmp<<endl;
}
return 0;
}
Comment: I find that if I use flib.seekp(some_number, ios_base::cur); with nonzero some_number, it works. And I use vs2012 express compiler, is it a bug?

File streams use a basic_filebuf<> for the stream buffer. The C++03 standard has this to say about class basic_filebuf<charT,traits>:
27.8.1.1 Class tempate basic_filebuf
The class basic_filebuf associates both the input
sequence and the output sequence with a file.
The restrictions on reading and writing a sequence controlled by an
object of class basic_filebuf are the same as for
reading and writing with the Standard C library FILEs.
In particular:
- If the file is not open for reading the input sequence cannot be read.
- If the file is not open for writing the output sequence cannot be written.
- A joint file position is maintained for both the input sequence and the output sequence.
Unfortunately it doesn't call out that when transitioning between reading and writing to a FILE object using the standard C library, you have to perform a file positioning call (or an fflush() when transitioning from write operations to read operations). See https://stackoverflow.com/a/14879076/12711.

Related

unable to read and write using same fstream object in c++

I'm trying to use same fstream object for first write the file and after that read the file.
when I'm using below code then the codes of writing the file is working but I'm getting junk output instead of texts which written in the file.
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
int main() {
fstream file;
file.open("test.txt",ios::in|ios::out| ios::trunc);
if (!file) {
cout << "Error";
}
else {
cout << "success";
file <<"\n\n1st Line\n 2nd line \n 3rd line\n";
string filecontent;
while (file.good()) {
getline(file, filecontent);
cout << filecontent << endl;
}
}
return 0;
}
Output
This code has two separate problems. The first (which others have already pointed out to at least some degree) is that your loop isn't detecting the end of the file correctly. In fact, almost any time you use while (!file.eof()) or while (file.good()), it's going to be a mistake--it won't detect end of file at the right time, so (for example) when you reach the end of the file, you won't detect it at the right time, and you'll see the last item in the file appear to be read twice before the loop exits.
In addition to that, however, you have a problem in that you're writing to the file, then immediately trying to read. That's simply not allowed--you want to do a seek any time you switch between reading and writing.
In this case, you have a bit of a further problem. Since you've just written data into the file, your file's current position is at the end of the file. So even if you could just start reading without seeking, you'd start reading from the end of the file. That, of course, would immediately fail.
So you also really need to seek back to the beginning of the file to be able to read it back in.
So, the big changes here are adding a line like: file.seekg(0); after you finish writing, but before you start to try to read that data back in, and then changing your reading loop to something like:
while (getline(file, filecontent)) {
cout << filecontent << endl;
}
One last point: although it's not going to make a big difference in this case, I'd advise using "\n" instead of std::endl. std::endl writes a new-line and flushes the file buffer. When you're writing to the screen it won't make any real difference, but when writing to a normal file flushing the buffer unnecessarily can and will slow your code substantially (10x slower is pretty common).

Reading RAW audio file

I am a beginner in C++ and I have a school project implying analysing an audio file and I can't read the file, in the example the program keeps reading the first value from the file
#include <iostream>
#include<fstream>
using namespace std;
int main()
{
ifstream c;
c.open("cv.RAW");
int b;
while(1)
{
c>>b;
cout<<b<<" ";
}
return 0;
}
you need to
open your file in binary mode ifstream c("cv.RAW", std::ios_base::binary), if you read a binary file without this flag it will be treated as a text file and will give you incorrect values.
check the file is open by checking if (c.good()) or just if (c) before reading any values
check the reads succeed by again checking if (c.good()) or just if (c) after reading each byte
read using c.read((char*)&b, sizeof(b)) if you want to read an integer at a time, for a single byte use char ch; c.read(&ch, sizeof(ch))
iostreams don't throw exceptions by default so you always need to check their state every time you use them.

'Access violation' using fstream

#include <iostream>
#include <fstream>
#include <conio.h>
using namespace std;
int main()
{
char r;
fstream file1("text.txt", ios::in |ios::binary);
fstream file2("text.txt", ios::out | ios::binary);
r='r';
for(int i=0; i<100; i++)
file2.write((char*)r, sizeof(char));
while(!file1.eof())
{
file1.read((char*)r, sizeof(char));
cout<<r<<"\n";
}
file1.close();
file2.close();
getch();
}
when I run this in vc++ 2010, I get the following error during run time:
Unhandled exception at 0x55361f68 (msvcp100d.dll) in file io.exe: 0xC0000005: Access violation reading location 0x00000072.
what could be causing this error? this happens while reading the line :
file2.write((char*)r, sizeof(char));
did I make any mistake? If yes please point it out for me (thanks in advance).
Update: I am still not getting my expected output (after correcting (char*)r to (char*)&r). the output that I get is just: r. shouldn't I expect 100 characters to be displayed starting from 'r'? If not, please tell me why and thanks in advance.
You need
file1.read((char*)&r, sizeof(char));
or
file1.read(&r, sizeof(char));
In addition to the other answer, there's also another problem your code has.
Streams perform buffered I/O by default; when writing into file1, the contents that you've written probably haven't been outputted to the actual file yet. The contents are actually stored in a temporary buffer for efficiency. Writing to the actual file is an operation reserved for an explicit flush(), when close() is called, or when the file stream goes out of scope and is destructed.
The problem in your code is that directly after writing to the file stream, you perform input without determining whether that output data was written to the actual file. This can cause Undefined Behavior if you assume that the data was read successfully from the input file to the variable.
File streams that depend on each other should be synchronized. Namely, when a file stream is trying to read from the same file that you have written to, then the output file stream must be flushed. This can be facilitated by "tying" the streams together, this is done using tie():
file1.tie(&file2);
When file1 performs input, file2 will then be flushed, forcing the data in its buffer to be written the file.
Another problem you have is that you don't check if the file streams were constructed correctly, or that you have successfully read from file1. You can use if() statements to do this:
std::fstream file1("text.txt", std::ios_base::in | std::ios_base::binary);
std::fstream file2("text.txt", std::ios_base::out | std::ios_base::binary);
char r('r');
if (file1 && file2)
{
file1.tie(&file2);
for (int i = 0; i < 100; ++i)
file2.write(&r, sizeof(char));
while (file1.read(&r, sizeof(char))) {
std::cout << r << std::endl;
}
}
You started reading from a file immediately after writing on that file and without closing the write file stream. Until you close the write file stream it will not commit the writings. So there is a change of getting access violation as it holds the control.
Try following code
#include <iostream>
#include <fstream>
#include <conio.h>
using namespace std;
int main()
{
char *r="r";
fstream file2("text.txt", ios::out | ios::binary);
for(int i=0; i<100; i++)
file2.write((char*)r, sizeof(char));
file2.close();
fstream file1("text.txt", ios::in |ios::binary);
while(!file1.eof())
{
char rr;
file1.read(&rr, sizeof(char));
cout<<rr<<"\n";
}
file1.close();
getch();
}
You have tried to cast a single char to char * and also tried to read using fread without passing r's address. That's why the problem is occurring. Please carefully see my code above, it will fix your issues.

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++ edit a binary file with another

Solved! thanks all of you very much. My day has been made!(well morning, its 4am)
I'm trying to write a program in C++ that opens a .dat file in binary and replaces the first 1840 hex characters with that of another .dat file, while leaving the remaining hex values of the first .dat file the same. I have spent about 12 hours on this today and have had little success. I am a beginner programmer, I have taken one semester worth of c++ courses and we did not get to streams.
(it opens a file and everything, but deletes every thing after the new values have been added)
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <cmath>
#include <cstring>
using namespace std;
int main (){
string filename;
long size;
char* memblock;
cout << " Enter a file to be modded by Mod.dat ";
cin >> filename;
ofstream infile ( filename ,std::ofstream::binary);
//filename: the file that will be opened and changed)
ifstream modFile ("Mod.dat", ifstream::binary);
// (mod.dat is the file that i get the first 1840 hex values from)
modFile.seekg (0,modFile.end);
size = modFile.tellg();
memblock = new char [size];
modFile.seekg (0, ios::beg);
modFile.read (memblock, size);
infile.write(memblock, 1840);
modFile.close();
infile.close();
cout << endl;
return 0;
}
Any help would be greatly appreciated, I hope there is some simple way to do this.
Solved! thanks all of you very much. My day has been made!(well morning, its 4am)
Edit:
You can modidy your file in place with something like :
std::fstream s(my_file_path, std::ios_base::binary);
s.seekp(position_of_data_to_overwrite, std::ios_base::beg);
s.write(my_data, size_of_data_to_overwrite);
std::fstream will not truncate your input file as std::ofstream does.
The other solution is to not use the same file for reading and writing. Use three files :
One for the output file.
One for the First input file.
One for the second input file.
fstream infile ( filename ,std::ofstream::binary); does not keeps the contents of the original file. Everything you write will erase the contents of the file.
Thus, you should:
open the output file
open the "Mod" file, read the first 1840 bytes from the first file, write them into the output file.
open the "main input file" file, move the cursor to 1840, read the remaining data and write it to the output file.
Depending on the "main input file" size, you may want to buffer you read/write operation.
My preferred fix, although Matthieu Rouget's fix does indeed work, is to just add ofstreeam::in to the opening of the input file:
ofstream infile ( filename.c_str(), std::ofstream::binary | ofstream::in);
(I had to use c_str() in my build, as glibc in my version doesn't take std::string as input).
I tested this on my local system (it took a while to realize that mod.dat is actually "Mod.dat"!)
It is probably a good idea to also check that the files actually opened, so something like this after ofstream infile line:
if (!infile)
{
cout << "Couldn't open " << filename << endl;
}
and similar for the modfile line.
And since you go through the effort of figuring out what the first part of the modfile size is, I would suggest that you also USE that for the writing of the file.