How to truncate a file while it is open with fstream - c++

I know it is possible to truncate a file with
std::fstream fs(mypath, std::fstream::out | std::fstream::trunc);
but I need to read the file, truncate it, then write new contents all with the same file handle (so the whole operation is atomic). Anyone?

I don't think you can get "atomic" operation but using the Filesystem Technical Specification that has now been accepted as part of the Standard Library (C++17) you can resize the file like this:
#include <fstream>
#include <sstream>
#include <iostream>
#include <experimental/filesystem> // compilers that support the TS
// #include <filesystem> // C++17 compilers
// for readability
namespace fs = std::experimental::filesystem;
int main(int, char*[])
{
fs::path filename = "test.txt";
std::fstream file(filename);
if(!file)
{
std::cerr << "Error opening file: " << filename << '\n';
return EXIT_FAILURE;
}
// display current contents
std::stringstream ss;
ss << file.rdbuf();
std::cout << ss.str() << '\n';
// truncate file
fs::resize_file(filename, 0);
file.seekp(0);
// write new stuff
file << "new data";
}

File streams don't support truncation except when opening a file. Also, the operations wouldn't be "atomic" anyway: at least, on POSIX systems you can happily read and write a file already opened by another process.

C++ 11 supports swap on ofstream. The best that I could imagine doing would be to open an empty file and call swap. This would not be atomic, but as close as you can get.

Related

std::getline with std::fstream

I am using std::fstream to read and write to the same file. I can see the write happening but not the read.
After searching the web, I got to know that I can not set in and app mode together. So, got rid of that and made it very simple of not passing any arguments.
I am very interested to know the reason why read is not happening.
Also, how do people read and write to the same file using same fstream?
My code:
#include<iostream>
#include<fstream>
int main() {
std::fstream* fs = new std::fstream("xx.txt");
*fs << "Hello" << std::endl;
(*fs).close(); // ?
std::string line;
while(std::getline(*fs, line)) {
std::cout << line << std::endl;
}
}
With this code, I can xx.txt contain "Hello" as its content but it does not go inside the while loop at all stating that reading failed.
How can I overcome this?
You forgot to reopen the stream. Actually you can't open a stream in both directions (at the same time).
So the steps are:
Open the stream for writing
Write data
Close the stream
Reopen the stream for reading
Read data
Close it (optional)
Your sample can be rewritten as:
#include <fstream>
#include <iostream>
int main()
{
const std::string file_path("xx.txt");
std::fstream fs(file_path, std::fstream::app);
if(fs) // Check if the opening has not failed
{
fs << "Hello" << std::endl;
fs.close();
}
fs.open(file_path, std::fstream::in);
if(fs) // Check if the opening has not failed
{
std::string line;
while(std::getline(fs, line))
{
std::cout << line << std::endl;
}
fs.close();
}
return 0;
}
Note that it is a good idea to check if the stream is successfully open before trying to use it.
I will try to explain the issue.
Statement std::fstream* fs = new std::fstream("xx.txt"); will open file if it exists in default mode "in|out" .
If the file does not exist then the call to open from inside of constructor std::fstream will fail. And this can be checked by checking failbit using function fail(). So you will explicitly need to call 'open' to use the fstream object for data input. Note: the new file will not be created unless you call 'close'.
You can test this by actually trying to open an existing file or new file you can see the difference.
So alternatively what you should do is always call 'open' which will work in both cases (if file exists or not).
#include<iostream>
#include<fstream>
int main() {
//std::fstream fs("xx.txt");
//std::cout << fs.fail() << std::endl; // print if file open failed or passed
std::fstream fs;
fs.open("xx.txt", std::fstream::in | std::fstream::out | std::fstream::app);
std::cout << fs.fail() << std::endl;
fs << "Hello" << std::endl;
if (fs.is_open())
{
std::cout << "Operation successfully performed\n";
fs.close();
}
else
{
std::cout << "Error opening file";
}
For reading the content of the file you will first need to close the file. And then reopen and read. As I understand once you start using the object fs for insertion you cannot read from it unless you explicitly close it and reopen.
fs.open("xx.txt", std::fstream::in | std::fstream::out);
std::string line;
while(std::getline(fs, line)) {
std::cout << line << std::endl;
}
std::cout << "end" << std::endl;
fs.close();
}

C++ ofstream dynamic file names and content

Trying to write a dynamic file name and content using fstream with the following:
ofstream file;
file.open("./tmp/test.txt");
//file.open("./tmp/%s.txt.txt", this->tinfo.first_name); //nope file.open->FUBAR
//file.open("./tmp/" + this->tinfo.first_name + ".txt"); //nope this->FUBAR
//file.write( "%s\n", this->tinfo.first_name); //nope this->FUBAR
file << "%s\n", this->tinfo.first_name; //nope %s->FUBAR
//Me->FUBU
file << "test\n";
file << "test\n";
file.close();
I was naive enough to assume the printf (%d, this->foo) conventions will work, if not for the actual file name, then for the content.
Nothing seems to work, what am i missing?
just in case its something in my includes:
#include "stdafx.h"
//#include <stdio.h> //redundant, as "stdafx.h" already includes it
#include <stdlib.h> /* srand, rand */
#include <time.h> /* time */
#include <iostream>
#include <fstream>
#include <string>
if this->tinfo.first_name is a std::string you can append everything into one string.
std::string temp = "./tmp/" + this->tinfo.first_name + ".txt";
file.open(temp);
If not, building a string with a std::stringstream
std::ostringstream temp;
temp << "./tmp/" << this->tinfo.first_name << ".txt";
file.open(temp.str());
should handle any data type that %s will work with.
Documentation for std::ostringstream
Note: file open that can consume a std::string was added in C++11. If compiling to an older standard you will need
file.open(temp.c_str());
You don't need %s this case, the ofstream will implicitly understand this->tinfo.first_name. So please replace this line
file << "%s\n", this->tinfo.first_name; //nope %s->FUBAR
by
file << this->tinfo.first_name << "\n"; //nope %s->FUBAR
I don't see why you would want to use printf syntax with fstream. I would simply suggest using ofstream the same way you would use cout. E.X:
file << this->tinfo.first_name << '\n';

How often should I check whether an fstream object is open?

I use an fstream object to write data to a text file. I write some initial data to it once and then write more data to it in a loop. Every time before writing to the file stream, I check whether it's open. Is this unnecessary? Should I only check once immediately after creating the fstream object?
Basically, I do this:
#define STOP 32700 // some value that indicates no more data is available
#include <string>
#include <exception>
#include <fstream>
int main (int argc, char* argv[])
{
try {
double data[5] {};
const std::string output_file_name {"name.txt"}
std::fstream outputFile (output_file_name, std::ios::out | std::ios::trunc);
if (outputFile.is_open()) // successfully opened file
outputFile << "initial text\n";
else // if text file could not be opened
throw Fstream_Exception;
/* do some other stuff (in various threads) */
do { // ok, now get data and write it to the file!
getData(&data[0]);
if (outputFile.is_open())
outputFile << data[0] << '\n';
else
throw Fstream_Exception;
} while (data[0] != STOP);
}
catch (Fstream_Exception& fstream_exception) {
/* handle exception */
}
}
The stream itself can throw exceptions when an error occurs. Just use its exceptions() method and pass the types of errors you want it to detect. This is more convenient than checking the state flags after each operation.

Not able to ofstream using __gnu_cxx::stdio_filebuf

This creates the file but it doesn't write anything.
std::ofstream outstream;
FILE * outfile;
outfile = fopen("/usr7/cs/test_file.txt", "w");
__gnu_cxx::stdio_filebuf<char> filebuf(outfile, std::ios::out);
outstream.std::ios::rdbuf(&filebuf);
outstream << "some data";
outstream.close();
fclose(outfile);
I know there are other easy solutions to achieve the output, but i need to use this non-standard filebuf to lock a file while editing, so that other process can't open the file.
I don't know why this is not working.
std::ostream already has a constructor doing the right thing:
#include <ext/stdio_filebuf.h>
#include <iostream>
#include <fcntl.h>
int main() {
auto file = fopen("test.txt", "w");
__gnu_cxx::stdio_filebuf<char> sourcebuf(file, std::ios::out);
std::ostream out(&sourcebuf);
out << "Writing to fd " << sourcebuf.fd() << std::endl;
}
Remember that stdio_filebuf won't close the FILE* when it is destroyed, so remember to do that yourself if you need it.

How to create a file in a different directory in C++?

#include <iostream>
#include <fstream>
#include <cstdlib>
int main() {
std::fstream f1("/tmp/test");
if (!f1) {
std::cerr << "f1 failed\n";
} else {
std::cerr << "f1 success\n";
}
FILE *f2 = fopen("/tmp/test", "w+");
if (!f2) {
std::cerr << "f2 failed\n";
} else {
std::cerr << "f2 success\n";
}
}
Creating a file in /tmp/ doesn't work for me using fstreams but it does with fopen. What could be the problem? (I get f1 failed and f2 success when /tmp/test doesn't already exist)
You have to tell the fstream you are opening the file for output, like this
std::fstream fs("/tmp/test", std::ios::out);
Or use ofstream instead of fstream, that opens the file for output by default:
std::ofstream fs("/tmp/test");
Your first method call does not automatically create a file: see fstream.
If you want your first method call to create a file, use:
std::fstream f1("/tmp/test", fstream::out);
I don't know which is the default mode for the fstream constructor, I tried with this and it worked
std::fstream f1("/tmp/test", std::fstream::in | std::fstream::out);
It creates a file for input and output, you should check the fstream documentation here