I just started working with binary files in C++, and i have successfully written and read a (.bin) file. Here is the code:
#include <iostream>
#include <cstring>
#include <fstream>
using namespace std;
int main()
{
char input[100];
strcpy(input, "This is a string");
fstream file("example.bin", ios::binary | ios::in | ios::out |
ios::trunc);
if(!file.is_open())
{
cerr << "Error opening file.\n";
} else {
for(int i = 0; i<= strlen(input); i++)
{
file.put(input[i]);
}
}
file.seekg(0);
char ch;
while(file.good())
{
file.get(ch);
cout<<ch;
}
}
And this worked. After that, i tried to redesign the code to just read a binary file. The major changes were: changed fstream to be an ifstream(to read), deleted the part with writing into a file. Once the code was ready, i found a file i want to read (eof0.bin). When i used the code, the only thing i got was an empty string. I noticed that the initial size of the file was 37 kilobytes, while after using my program it became 0. I want to know, how my program cleared the data in the binary file?
This is the code that i used to read the file.
#include <iostream>
#include <cstring>
#include <fstream>
using namespace std;
int main()
{
ifstream file("eof0.bin", ios::binary | ios::in | ios::out | ios::trunc);
if(!file.is_open())
{
cerr << "Error opening file.\n";
} else {
// Nothing.
}
file.seekg(0);
char ch;
while(file.good())
{
file.get(ch);
cout<<ch;
}
}
Everything compiles, but using it on a file 37 kilobytes in size gives me a 0 kilobyte file.
You open with an openmode std::ios_base::trunc. From http://en.cppreference.com/w/cpp/io/ios_base/openmode we can see that it
discard[s] the contents of the stream when opening
So just use:
// also dropped ios::out since you only want to read, not write
ifstream file("eof0.bin", ios::binary | ios::in);
Further, this
char ch;
while(file.good())
{
file.get(ch);
cout<<ch;
}
is not an appropriate way to read the file. Think about what happens with an empty file: After opening it, it's "good" (remember, the eofbit is only set when some input operation encounters eof). Then the get fails, leaving ch as it is, thus invoking undefined behavior. Better test on the stream state directly after the input operation:
char ch;
while (file.get(ch)) {
// use ch
}
// optionally distinguish eof and fail cases
For more background on reading files, see Why is iostream::eof inside a loop condition considered wrong?
Related
I am learning how to read and write from file . There is a problem that when I try to write (--something in the file letter for example--) after reading or read after writing in the file
using fstream
something wrong is happening. I tried to just write or read and it worked. what is the problem?
the file content is :
abcdefgh
ijklmnopqr
stuvw
xyz
and the code is :
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
fstream ioFile;
char ch;
ioFile.open("search.txt", ios::in | ios::out);
if (!ioFile)
{
cout << "problem opening the file";
goto k270;
}
while (ioFile>>ch)
{
if (ch == 'z')
{
ioFile.seekp(((int)ioFile.tellg()));
ioFile << "x";
}
}
//cout<<ioFile.rdbuf();
ioFile.close();
k270:
system("pause");
return 0;
}
Look at this answer: https://stackoverflow.com/a/17567454/11829247 it explains the error you are experiencing.
Short version: Input and output is buffered and interleaving reads and writes only work if you force buffer updates in between.
This works for me:
#include <iostream>
#include <fstream>
#include <string>
int main()
{
std::fstream ioFile;
char ch;
ioFile.open("search.txt", std::ios::in | std::ios::out);
if (!ioFile)
{
std::cout << "problem opening the file";
return 1;
}
while (ioFile >> ch)
{
if (ch == 'z')
{
ioFile.seekp(-1, std::ios_base::cur);
ioFile << "x";
ioFile.flush();
}
}
ioFile.close();
return 0;
}
The difference is that I use ioFile.seekp(-1, std::ios_base::cur); to move one step back from the current position. You could also use ioFile.seekp((int)ioFile.tellg() -1); - note the -1.
Then after stepping back and overwriting the z, use ioFile.flush(); to force the write to be pushed to file. This also means that the read buffer is updated, without this the read operation just steps back in its buffer and keeps reading the same buffered z.
i tried to open and read an application in binary mode and get 100 characters(For a large file, i did this so that i could read all the characters) in binary mode and then transfer them to new file(in fact, this program will be the same as the previous program, but with a different name ) to find out if it works properly or not
so anyway my source code is:
#include <iostream>
#include <vector>
#include <fstream>
using namespace std;
int main()
{
//Vector of 100 characters initialised to 0
vector<char> ch(100, 0);
ifstream file("example.exe",ios::in|ios::binary);
if (file.is_open())
{
while (file)
{
file.read(ch.data(), 100);
// Get the number of bytes actually read
size_t count = file.gcount();
ch[file.gcount()] = '\0';
//cout << ch.data() << endl;
ofstream output("output.exe", ios::out | ios::binary | ios::app);
output.write(ch.data(), sizeof(100));
output.close();
}
}
file.close();
}
my problem is the output with the correct information is not included and the size is smaller than the original application(example.exe) what is happening?
The line output.write(ch.data(), sizeof(100));
should be replaced with output.write(ch.data(), count);
Since sizeof(100) only returns 4, which is the size in bytes of an integer.
You should also remove the line ch[file.gcount()] = '\0'; since file.gcount() might be out of bound.
I just tested it and this works for me
#include <iostream>
#include <vector>
#include <fstream>
using namespace std;
int main()
{
vector<char> ch(100, 0);
ifstream file("app.exe",ios::in|ios::binary);
if (file.is_open())
{
ofstream output("app_copy.exe", ios::out | ios::binary | ios::trunc);
while (file)
{
file.read(ch.data(), 100);
output.write(ch.data(), file.gcount());
}
output.close();
}
file.close();
}
Notice that ios::app is replaced by ios::trunc, which will delete the content of the file when opening.
I have written a program which opens a file then displays line by line its contents (text file)
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main (int argc, char* argv[])
{
string STRING;
ifstream infile;
infile.open(argv[1]);
if (argc != 2)
{
cout << "ERROR.\n";
return 1;
}
if(infile.fail())
{
cout << "ERROR.\n";
return 1;
}
else
{
while(!infile.eof())
{
getline(infile,STRING);
cout<<STRING + "\n";
}
infile.close();
return 0;
}
}
What do I need to add to make the file be read only ?
(infile.open(argv[1]) is where am guessing something goes)
The class ifstream is for reading only so, problem solved. Also, did you really mean to check argc after using argv[1] ?
On the other hand, when you use fstream you need to specify how you want to open the file:
fstream f;
f.open("file", fstream::in | fstream::out); /* Read-write. */
The default mode parameter of open for ifstream class is ios::in. That is
infile.open(argv[1]);
is same as:
infile.open(argv[1], ios::in);
So you are opening the file in read-only mode.
You already open the file for read-only. Your can't write to it if you use ifstream. Even:
infile.rdbuf()->sputc('a');
is guaranteed to fail.
You don't need to do anything as the default value for the openmode is already ios_base::in. So you're already good to go :)
See here for more details: http://en.cppreference.com/w/cpp/io/basic_ifstream/open
I'm trying to open a binary file for writing without erasing the content. But I do not want to write to eof. I want to write to a specific position in file.
Here is a litte example:
ofstream out("test.txt", ios::binary | ios::app);
for(int i = 0; i < 100; i++)
out.put('_');
out.write("Hallo", 5);
out.close();
ofstream out2("test.txt", ios::binary | ios::app);
out2.seekp(10);
out2.write("Welt", 4);
out2.close();
If using app, seek doesn't work. If not using app opening file erases data. Does anybody know an answer?
try the second overload of seekp, which allows you to provide an offset and a direction, this could be begining of file in your case (i.e. ios_base::beg). This of course assumes you know what you are doing and all you want to do is overwrite an existing number of characters.
EDIT: here is fully working example:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
{
ofstream out("test.txt", ios::binary);
for(int i = 0; i < 100; i++)
out.put('_');
out.write("Hallo", 5);
}
{
fstream out2("test.txt", ios::binary | ios::out | ios::in);
out2.seekp(10, ios::beg);
out2.write("Welt", 4);
}
}
When opening with ios::app, it is as if you open a new file that just happened to be attached to an existing file: you can not access the existing file. I'm not sure, because I would do as in Kerrek's answer, but if you really want to try, you probably have to open with "ios::in | ios::out", similar to fopen("test.txt", "rw").
Or as crashmstr points out: ios::out might be enough.
You cannot magically extend the file from the middle. Perhaps easiest to write to a new file: First copy the initial segment, then write your new data, then copy the remaining segment. When all is done, you can overwrite the original file.
According to the specification of fstream here
fstream::open
the ios::app "Sets the stream's position indicator to the end of the stream before EACH output operation." So ios::app doesn't work for replacing, seeks of any sort fail, at least for me.
Just using ios::out does wipe out the file contents preserving only the size, basically turning the file into trash.
ios::in|ios::out turned out as the only working thing for me.
Working Code: This code searches for a string (OLD-STRING) in cout.exe and replaces with a new string (NEW-STRING).
`#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(int argc, char *argv[])
{
fstream ifs;
ifs.open ("C:\\Users\\user\\Desktop\\cout.exe", fstream::binary | fstream::in | fstream::out);
std::string str((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
size_t pos = str.find("OLD-STRING");
if (pos != string::npos)
{
cout << "string found at position: " << int(pos) << endl;
ifs.seekp(pos);
ifs.write("NEW-STRING", 10);
}
else
{
cout << "could not find string" << endl;
}
if (ifs.is_open())
ifs.close();
return 0;
}`
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
int main(int argc,char *argv){
fstream inout("C:\\Users\\7\\Desktop\\test.txt",ios::in | ios::out | ios::binary);
if (!inout){
cout<<" cannot open input file.\n";
return 1;
}
long e,i,j;
char c1,c2;
e=5;
for (i=0,j=e;i<j;i++,j--){
inout.seekg(i,ios::beg);
inout.get(c1);
inout.seekg(j,ios::beg);
inout.get(c2);
inout.seekp(i,ios::beg);
inout.put(c2);
inout.seekg(j,ios::beg);
inout.put(c1);
}
inout.close();
return 0;
}
why this code writes can't open file
EDIT:
i have made corrections but here is one problem
in test.txt is written such thing
maiko
miyvarxar
shen
me
so it should write
me shen miyvarxar maiko
but it does not write anything
please help
This seems to work for me:
using namespace std;
int main()
{
fstream inout("C:\\Users\\turdfurguson\\Turds\testfile.txt", ios::in | ios::out | ios::binary);
if (inout.good())
cout << "OK!" << endl;
}
Provided you have a "C:\Users\turdfurgson\Turds\testfile.txt" file that is readable and writable.
The code you've provided looks fine.
You may have supplied the wrong path or something like that.
You could also try attempting to open that file in read only mode
and see if that is ok:
std::ifstream in("path", std::ios::binary);
if (!in) {
// fail
}