#include<fstream>
#include<iostream>
using namespace std;
int main()
{
int i = 20;
fstream fs("someFile.dat", ios::out | ios::binary | ios::in);
if(!fs)
{
cout << "FILE COULD NOT BE OPENED" << endl;
}
fs.write(reinterpret_cast<const char*>(&i),sizeof(int));
i = 0;
fs.read(reinterpret_cast<char*>(&i),sizeof(int));
cout << i << endl; // shows 0
}
The 'i' in cout at the last should display 20 but it shows 0.
After writing to the file, you are at the end of the file.
You can figure this out using tellg, or "tell get":
std::cout << "Position in file is: " << fs.tellg() << std::endl;
This will tell you the byte offset you are within the file, from the start of the file. You need to seek the appropriate position in the file first, before you can read bytes from the file. To do so, we can use seekg, or "seek get".
fs.seekg(0);
This seeks the beginning of the file (byte offset of 0 from the start of the file), so you should be able to read from the file correctly.
For your example, seekg and seekp should be identical, as are tellg and tellp, but you should ideally use the member functions ending in "g" (for "get") for input streams, and the functions ending in "p" (for "put") for output streams.
EDIT
A good point was raised in the comments by #Borgleader, for more complicated examples, you may not know if a read failed. To do so, you can check the fail bit:
if (fs.fail()) {
// you can check more specific error codes with std::ios_base::iostate
// fs.fail() will evaluate to 0 if no error, or false, otherwise it has an error
std::cout << "Failed to read from file" << std::endl;
}
UPDATE
To analyze iostate flags, you can use the fstream member functions good, eof, fail, bad. A quick example checking the iostate of an fstream for the original example follows:
#include <fstream>
#include <iostream>
int main()
{
int i = 20;
std::fstream fs("someFile.dat", std::ios::out | std::ios::binary | std::ios::in);
fs.write(reinterpret_cast<const char*>(&i), sizeof(int));
i = 0;
fs.read(reinterpret_cast<char*>(&i), sizeof(int));
// you can check other settings via the ios::fail() member function
if (fs.good()) { // checks goodbit
std::cout << "File is normal, no errors\n";
}
if (fs.eof()) { // checks end of file
std::cout << "End of file\n";
}
if (fs.fail()) { // checks failbit or badbit
std::cout << "Failed to read, failbit\n";
}
if (fs.bad()) { // checks the badbit
std::cout << "Failed to read, badbit\n";
}
}
This, when run produces the following output:
End of file
Failed to read, failbit
Overall, often checking if the read fails is sufficient, unless you need to further refine your logic.
Related
I've been trying to write a program to open a file in both read and write mode:
#include <fstream>
#include <iostream>
using namespace std;
int main(){
fstream obj;
obj.open("hello.txt",ios::in|ios::out);
if (!obj){
cout << "File not opened" <<endl;
return 1;
}
obj << "Hi How are you" ;
char c;
while (!obj.eof()){
obj.get(c);
cout << c;
}
obj.close();
return 0;
}
When I compile this program on Visual Studio Code on Windows, though the text "Hi how are you" is printed in the file, the contents of the file are not printed on my screen. Can someone tell me what might be the problem?
Resetting the position indicator with seekp to 0 helps, because both output and input indicators are set to the end of file after write operation (you can read them with tellp tellg).
obj << "Hi How are you" ;
obj.seekp(0);
char c;
while (!obj.eof()){
obj.get(c);
cout << c;
}
Considering avoiding using obj.eof(), you can e.g. read your file line by line:
std::string line;
std::getline(obj, line);
std::cout << line << std::endl;
or in the loop:
while (std::getline(obj, line)) // here std::basic_ios<CharT,Traits>::operator bool is used to check if operation succeeded
{
std::cout << line << std::endl;
}
You got two problems there: buffering and seek position.
Buffering:
When you write the text with obj << "Hi How are you, you just write it into the buffer and the text gets written into the file after flushing the buffer. You can adjust which buffer type you want to use. The easiest way is to write std::endl after your text if you use line buffering.
A better explaination is already here
Seek Position:
You are reading from the last position in your file. You have to manually change the read position to the first character in the file, then you are done.
I'm trying to write a program that replaces a specific number with an 'x' character. The task requires every number to be in its own line, but it seems like '\n' is causing the read/write pointers to behave out of this world. Here's a picture of the output.
My questions are:
why are the pointers behaving this way?
How far do I need to move the write pointer backwards to overwrite a line to make this work?
is there an easier workaround?
Here's my code:
void input(int n)
{
fstream file;
file.open("numbers.txt", ios::out);
while(n --> 0)
{
file << n;
file << '\n';
}
file.close();
}
void read()
{
fstream file;
string tmp;
file.open("numbers.txt", ios::in);
while(true)
{
getline(file,tmp);
if(file.eof())
break;
cout << tmp << endl;
cout << "tellg: " << file.tellg() << " tellp: " << file.tellp() << endl;
}
file.close();
}
void replace()
{
fstream file;
string tmp;
file.open("numbers.txt", ios::in | ios::out);
while(true)
{
file >> tmp;
if(tmp == "6")
{
//cout << file.tellg() << endl;
file.seekp(file.tellg() - tmp.length()-1);
file << "x";
}
if(file.eof())
break;
}
file.close();
}
int main()
{
input(10);
replace();
read();
return 0;
}
Since you open your file in text mode, you need to account for the potential that the underlying stream may use a line end sequence (\r\n) rather than just a \n. I guess, this is the primary problem. The easiest remedy is probaly to open the file in binary mode:
file.open("numbers.txt", std::ios_base::binary | std::ios_base::in | std::ios_base::out);
That said, since you switch from writing to reading without intervening seek, your code is undefined behavior, i.e., anything can happen. You should seek to the current location between writing and reading.
Personally, I'd refrain from rewriting files in-place. It generally gets unnecessary trick. If I were to rewrite files in place, I'd use seekg() to get the current position before a read, saving the position and restoring it prior to the write (I essentially never use the seek operations, i.e., I may have got the signatures wrong):
for (std::streampos pos = (in >> std::ws).tellg();
in >> tmp; pos = (in >> ws).tellg()) {
if (need_to_overwrite) {
in.seekp(pos);
// ...
in.seekg(0, std::ios_base::cur);
}
}
The use of in >> std::ws is to make sure that whitespace is skipped before storing the position.
Also note that your check for file.eof() is wrong: the last line is processed twice. When reading from a file the result shall be tested before using the read string, e.g.:
while (in >> tmp) {
// ...
}
Edit: changed my question to be more accurate of the situation
I'm trying to open up a text file (create it if it doesnt exist,open it if it doesnt). It is the same input file as output.
ofstream oFile("goalsFile.txt");
fstream iFile("goalsFile.txt");
string goalsText;
string tempBuffer;
//int fileLength = 0;
bool empty = false;
if (oFile.is_open())
{
if (iFile.is_open())
{
iFile >> tempBuffer;
iFile.seekg(0, iFile.end);
size_t fileLength = iFile.tellg();
iFile.seekg(0, iFile.beg);
if (fileLength == 0)
{
cout << "Set a new goal\n" << "Goal Name:"; //if I end debugging her the file ends up being empty
getline(cin, goalSet);
oFile << goalSet;
oFile << ";";
cout << endl;
cout << "Goal Cost:";
getline(cin, tempBuffer);
goalCost = stoi(tempBuffer);
oFile << goalCost;
cout << endl;
}
}
}
Couple of issues. For one, if the file exist and has text within it, it still goes into the if loop that would normally ask me to set a new goal. I can't seem to figure out what's happening here.
The problem is simply that you are using buffered IO streams. Despite the fact that they reference the same file underneath, they have completely separate buffers.
// open the file for writing and erase existing contents.
std::ostream out(filename);
// open the now empty file for reading.
std::istream in(filename);
// write to out's buffer
out << "hello";
At this point, "hello" may not have been written to disk, the only guarantee is that it's in the output buffer of out. To force it to be written to disk you could use
out << std::endl; // new line + flush
out << std::flush; // just a flush
that means that we've committed our output to disk, but the input buffer is still untouched at this point, and so the file still appears to be empty.
In order for your input file to see what you've written to the output file, you'd need to use sync.
#include <iostream>
#include <fstream>
#include <string>
static const char* filename = "testfile.txt";
int main()
{
std::string hello;
{
std::ofstream out(filename);
std::ifstream in(filename);
out << "hello\n";
in >> hello;
std::cout << "unsync'd read got '" << hello << "'\n";
}
{
std::ofstream out(filename);
std::ifstream in(filename);
out << "hello\n";
out << std::flush;
in.sync();
in >> hello;
std::cout << "sync'd read got '" << hello << "'\n";
}
}
The next problem you'll run into trying to do this with buffered streams is the need to clear() the eof bit on the input stream every time more data is written to the file...
Try Boost::FileSystem::is_empty which test if your file is empty. I read somewhere that using fstream's is not a good way to test empty files.
i wrote a code in C++ where it opens a .txt file and reads its contents, think of it as a (MAC address database), each mac address is delimited by a (.), my problem is after i search the file for total number of lines , iam unable to return the pointer to the initial position of the file in here i use seekg() and tellg() to manipulate the pointer to the file.
here is the code:
#include <iostream>
#include <fstream>
#include <conio.h>
using namespace std;
int main ()
{
int i = 0;
string str1;
ifstream file;
file.open ("C:\\Users\\...\\Desktop\\MAC.txt");
//this section calculates the no. of lines
while (!file.eof() )
{
getline (file,str1);
for (int z =0 ; z<=15; z++)
if (str1[z] == '.')
i++;
}
file.seekg(0,ios::beg);
getline(file,str2);
cout << "the number of lines are " << i << endl;
cout << str2 << endl;
file.close();
getchar();
return 0;
}
and here is the contents of the MAC.txt file:
0090-d0f5-723a.
0090-d0f2-87hf.
b048-7aae-t5t5.
000e-f4e1-xxx2.
1c1d-678c-9db3.
0090-d0db-f923.
d85d-4cd3-a238.
1c1d-678c-235d.
here the the output of the code is supposed to be the first MAC address but it returns the last one .
file.seekg(0,ios::end);
I believe you wanted file.seekg(0,ios::beg); here.
Zero offset from the end (ios::end) is the end of the file. The read fails and you're left with the last value you read in the buffer.
Also, once you've reached eof, you should manually reset it with file.clear(); before you seek:
file.clear();
file.seekg(0,ios::beg);
getline(file,str2);
The error would have been easier to catch if you checked for errors when you perform file operations. See Kerrek SB's answer for examples.
Your code is making all sorts of mistakes. You never check any error states!
This is how it should go:
std::ifstream file("C:\\Users\\...\\Desktop\\MAC.txt");
for (std::string line; std::getline(file, line); )
// the loop exits when "file" is in an error state
{
/* whatever condition */ i++;
}
file.clear(); // reset error state
file.seekg(0, std::ios::beg); // rewind
std::string firstline;
if (!(std::getline(file, firstline)) { /* error */ }
std::cout << "The first line is: " << firstline << "\n";
I'm very new to the world of C++ error handling, but I was told here:
Checking for file existence in C++
...that the best way to checks for file existence was with a try-catch block. From my limited knowledge on the topic, this sounds like sound advice. I located this snippet of code:
http://www.java2s.com/Tutorial/Cpp/0240__File-Stream/Readafileintrycatchblock.htm
#include <fstream>
#include <iostream>
using namespace std;
int main ()
{
try{
char buffer[256];
ifstream myfile ("test.txt");
while (! myfile.eof() )
{
myfile.getline (buffer,100);
cout << buffer << endl;
}
}catch(...){
cout << "There was an error !\n";
}
return 0;
}
...but when I compile it using
g++ -Wall -pedantic -o test_prog main.cc
And run the program in a directory where test.txt does not exist, the prog keeps spitting out empty lines to the terminal. Can anyone figure out why?
Also is this a good way to check for file existence for a file you actually want to open and read from (versus just something where your indexing a bunch of files and checking them over)?
Thanks!
In C++ iostreams do not throw exeptions by default. What you need is
ifstream myfile("test.txt");
if(myfile) {
// We have one
}
else {
// we dont
}
By default the fstream objects do not throw. You need to use void exceptions ( iostate except ); to set the exception behavior. You can fetch the current settings using iostate exceptions ( ) const;. Change your code just a bit:
#include <fstream>
#include <iostream>
#include <stdexcept>
using namespace std;
int main ()
{
try{
char buffer[256];
ifstream myfile ("test.txt");
myfile.exceptions ( ifstream::eofbit | ifstream::failbit | ifstream::badbit );
while (myfile)
{
myfile.getline (buffer,100);
cout << buffer << endl;
}
myfile.close();
}catch(std::exception const& e){
cout << "There was an error: " << e.what() << endl;
}
return 0;
}
First of all, for the try block to do any good, you need to enable exceptions for the stream.
Second, a loop like:
while (! myfile.eof() )
Will lead to nothing but trouble, and you're seeing that here. The problem (in this case) is that when the file failed to open, eof will never be signaled -- you can't/don't reach the end of the file because there is no file. Therefore, your loop runs forever, on an existentialist search for the end of a nonexistent file. Fix the loop, and things get better in a hurry:
char buffer[256];
ifstream myfile ("test.txt");
while (myfile.getline(buffer,100))
{
cout << buffer << endl;
}
While you're at it, a bit more fixing wouldn't hurt (unless you really meant to use less than half of the space you allocated for your buffer):
char buffer[256];
ifstream myfile ("test.txt");
while (myfile.getline(buffer,sizeof(buffer)))
{
cout << buffer << endl;
}
Or, of course, eliminate the problem entirely:
std::string buffer;
ifstream myfile("test.txt");
while (getline(myfile, buffer))
cout << buffer << "\n";
Edit: note that none of these (at least currently) depends on exceptions at all. They're all set up to write a line to the output if we succeeded in our attempt at reading a line from the input. If the file didn't open, the body of the loop simply won't execute, because we won't be able to read from a file that didn't open. If we want to print an error message telling the user that the file didn't open, we'd have to handle that separately from what's above. For example:
ifstream myfile("test.txt");
if (!myfile) {
std::cerr << "File failed to open";
return FAIL;
}
while (std::getline(myfile // ...