How can the current file be overwritten? - c++

For the following code:
fstream file("file.txt", ios::in):
//some code
//"file" changes here
file.close();
file.clear();
file.open("file.txt", ios::out | ios::trunc);
how can the last three lines be changed so that the current file is not closed, but "re-opened" with everything blanked out?

If I am understanding the question correctly, you'd like to clear all contents of the file without closing it (i.e. set the file size to 0 by setting EOF position). From what I can find the solution you have presented is the most appealing.
Your other option would be to use an OS-specific function to set the end of file, for example SetEndOfFile() on Windows or truncate() on POSIX.
If you're only looking to begin writing at the beginning of the file, Simon's solution works. Using this without setting end of file may leave you in a situation where you have garbage data past the last position you wrote though.

You can rewind the file: put back the put pointer to the beginning of the file, so next time you write something, it will overwrite the content of the file.
For this you can use seekp like this:
fstream file("file.txt", ios::in | ios::out); // Note that you now need
// to open the file for writing
//some code
//"something" changes here
file.seekp(0); // file is now rewinded
Note that it doesn't erase any content. Only if you overwrite it so be careful.

I'm guessing you're trying to avoid passing around the "file.txt" parameter and are trying to implement something like
void rewrite( std::ofstream & f )
{
f.close();
f.clear();
f.open(...); // Reopen the file, but we dont know its filename!
}
However ofstream doesn't provide the filename for the underlying stream, and doesn't provide a way to clear the existing data, so you're kind of out of luck. (It does provide seekp, which will let you position the write cursor back to the beginning of the file, but that wont truncate existing content...)
I'd either just pass the filename to the functions that need it
void rewrite( std::ostream & f, const std::string & filename )
{
f.close();
f.clear();
f.open( filename.c_str(), ios::out );
}
Or package the filestream and filename into a class.
class ReopenableStream
{
public:
std::string filename;
std::ofstream f;
void reopen()
{
f.close();
f.clear();
f.open( filename.c_str(), ios::out );
}
...
};
If you're feeling over zealous you could make ReopenableStream actually behave like a stream, so that you could write reopenable_stream<<foo; rather than reopenable_stream.f<<foo but IMO that seems like overkill.

Related

How to append to a std::fstream after you got to the end (std::fstream::eof() is true)

I open a file like this (Because it's part of an exercise and it may require overwriting the file):
#include <fstream> //std::fstream.
std::fstream file("file.txt", std::ios::in | std::ios::out);
And let's say I have read a file until the end (To get to the end of the file).
std::string tmp_buff;
while(std::getline(file, tmp_buff)) {}
file.seekp(file.tellg());
Now I have got to the end of the stream, How do I append to the file from here. Because if I just try to write like regularly, it will fail (It will not actually write):
file << "Text";
The only solution I have found is to reopen the file at the end of the file:
if(file.eof())
{
file.close();
file.open("file.txt", std::ios::in | std::ios::out | std::ios::app);
file << '\n';
}
Any help would be appreciated.
First, there is no need to state std::ios::in and std::ios::out when using a fstream because they are there the default value in the constructor. (it is actually std::ios_base::in/out to be more exact. std::ios (std::basic_ios<char>) inherits from std::ios_base)
So std::fstream file(filename) works the same.
The problem here is how C++ streams work.
When the file is read completely, the eofbit is set. After that, another reading happens which will trigger the failbit because there is nothing to read and the stream's bool conversion operator returns false and it exits the loop.
The bits will stay on until they are cleared. And while they are on, the stream doesn't do anything.
So to clear them:
file.clear();
Will do the work. You can use the stream after that.

C++ file handle

I am trying to implement a file handle class similar to the one in Bjarne Stroustrup's FAQ page. (Scroll to "Why doesn't C++ provide a 'finally' construct".) Unlike his example, however, I want to use C++ file streams instead of a FILE*.
Right now, I am considering creating a FileHandleBase class, or something similarly named, and two derived classes—one for input files and one for output files. Below is the implementation I wrote as a proof-of-concept; keep in mind that it is very simple and unfinished.
class FileHandle {
public:
FileHandle(const char* fn, ios_base::openmode mode = ios_base::in | ios_base::out) {
file.open(fn, mode);
// Check to make sure file is open
}
FileHandle(const string &fn, ios_base::openmode mode = ios_base::in | ios_base::out) {
file.open(fn, mode);
// Check to make sure file is open
}
~FileHandle() {
file.close();
}
private:
fstream file;
};
I would like to know if this is a viable way of making a file handle, that is, whether my inheritance idea is good. I also want to know the best way to deal with the ios_base::openmode parameter because the C++ reference page for std::ifstream says this:
Note that even though ifstream is an input stream, its internal filebuf object may be set to also support output operations.
In what cases would an ifstream be used for output operations, and, similarly, when would an of stream be used for input operations; and should I restrict the options for the ios_base::openmode parameter for my file handle class(es)? That way my input file handle would only handle input operations, and the output version would only handle output operations.
In what cases would an ifstream be used for output operations, and, similarly, when would an ofstream be used for input operations
You would open an output file stream with an std::ios_base::in openmode and vice-versa for an input file stream if you would still like to perform those associated operations using the internal std::filebuf object, which is accessible by stream->rdbuf(). Note that the streams std::ofstream and std::ifstream will still be able to perform output and input respectively even if they are opened with opposite openmodes.
int main() {
std::ofstream stream("test.txt");
stream << "Hello" << std::flush;
stream.close();
stream.open("test.txt", std::ios_base::in);
char buffer[SIZE] = {};
stream.rdbuf()->sgetn(buffer, SIZE);
std::cout << buffer << std::endl;
}

How can I switch between fstream files without closing them (Simultaneous output files) - C++

I have a little C++ issue that I couldn't solve by browsing online. Here is my code (extracted):
if(File.is_open()) {
while(!File.eof()) {
i++;
getline(File,Line);
if(i>=2) { //Skip Headers
int CharCount=0;
for(int CharPosition=0; CharPosition<Line.size(); CharPosition++) {
if(Line[CharPosition]==',') {
Length=CharPosition;
break;
}
}
NameText=Line.substr(0,Length);
Path= Path_Folder + "\\" + NameText + ".csv";
if(!CheckExistance(Path.c_str())) {
fstream Text_File;
}
Text_File.open(Path, fstream::in | fstream::out | fstream::app);
Text_File<<Line<<"\n";
Text_File.close();
}
}
}
This code is working fine, but I would like to change the fact that it closes the Text_File every time it goes in the while loop.
Basically, this program split a big input file in a lot of smaller files. As my
smaller files get bigger and bigger, the execution gets slower and slower
(normal). My goal is then to let all the smaller files (Text_File) opened in
this while loop and just switch the fstream pointer (pointer?) from one to
another.
I tried to change as:
...
NameText=Line.substr(0,Length);
Path= Path_Folder + "\\" + NameText + ".csv";
if(!CheckExistance(Path.c_str())) {
fstream Text_File;
}
if(!Text_File.open()) {
Text_File.open(Path, fstream::in |fstream::out | fstream::app);
}
Text_File<<Line<<"\n";
\\Text_File.close();
...
But it is working on the same Text_File no matter what NameText is. So I am guessing that the pointer of the fstream Text_File doesn't change. What do I need to be then? Rest the pointer? How?
Thank you, all!
Not sure it is relevant but I am working with Microsoft Visual C++ 2010 Express.
In addition, I am not a programmer neither by education nor by living, so if you can explain it without too advanced words, I'll appreciate.
It looks like you would like to juggle the filebufs on an ostream object.
Now, the only obstacle is that ostream or basic_filebuf<char> aren't copyable types, so you can't put them into a map (by filename) directly. This is easily worked around by creating a little Holder type:
struct Holder {
Holder(std::string const& path)
: buf(std::make_shared<std::filebuf>())
{
buf->open(path.c_str(), std::ios::out | std::ios::app);
}
std::shared_ptr<std::filebuf> buf;
};
std::map<std::string, Holder> buffers;
Now the complete program (tested) would look like this:
#include <fstream>
#include <sstream>
#include <iostream>
#include <map>
#include <memory>
const std::string Path_Folder = ".";
int main()
{
std::istream& File = std::cin; // just for example
std::filebuf dummy;
std::ostream TextFile(&dummy);
struct Holder {
Holder(std::string const& path)
: buf(std::make_shared<std::filebuf>())
{
buf->open(path.c_str(), std::ios::out | std::ios::app);
}
std::shared_ptr<std::filebuf> buf;
};
std::map<std::string, Holder> buffers;
int i = 0;
std::string Line;
while(getline(File, Line))
{
if (i++<2)
continue; //Skip Headers
auto NameText = Line.substr(0, Line.find(','));
auto Path = Path_Folder + '/' + NameText + ".csv";
// open, only if not allready opened
auto found = buffers.find(NameText);
if (end(buffers) == found)
found = buffers.insert({ NameText, Path }).first;
TextFile.rdbuf(found->second.buf.get());
TextFile << Line << std::endl; // notice implicit std::flush in std::endl
}
// all files are automatically closed here
}
Three more notes:
files get automatically closed when the buffers map goes out of scope.
you might need to add explicit flushes when switching rdbuf() like this, if you don't end your lines with an implicit std::flush (like with std::endl).
dummy only exists to have an ostream object that we can switch the buffer of
I tested this with the following input:
Header Row #1
Header Row #2
Jack,1,some data
Jill,2,some more data
Jack,3,not reopening :)
Jill,4,jill still receiving output
Romeo,5,someone else reporting
Now, I got the following output: see it live at Coliru
/tmp$ rm *.csv
/tmp$ make && ./test < input.txt && tail *.csv
g++ -std=c++11 -Wall -g test.cpp -o test
==> Jack.csv <==
Jack,1,some data
Jack,3,not reopening :)
==> Jill.csv <==
Jill,2,some more data
Jill,4,jill still receiving output
==> Romeo.csv <==
Romeo,5,someone else reporting
Note: it looks like your Text_File is out of scope. I guess you declared it somwhere else in the code. So, this line is useless:
if(!CheckExistance(Path.c_str())){fstream Text_File;}
To access multiple file streams you can use this simple class which utilizes the std::map data structure:
#include <iostream>
#include <map>
#include <string>
#include <fstream>
class StreamWriter
{
typedef std::map<std::string, std::fstream> StreamMap;
static StreamMap Files;
public:
static std::fstream& GetFile(const std::string& filename)
{
std::fstream& stream = Files[filename];
if (!stream.is_open())
{
stream.open(filename, std::fstream::in
| std::fstream::out | std::fstream::app);
}
return stream;
}
};
StreamWriter::StreamMap StreamWriter::Files = StreamWriter::StreamMap();
Then, access to files is as simple as:
StreamWriter::GetFile("C:/sample1.txt") << "test";
That's it.
What I would do is use std::map or std::unordered_map to map names to fstream objects.
map<string, fstream> files;
...
while(getline(File,Line)) // don't use while(File.eof())
{
...
if( files.count(NameText) == 0 ) // checks for the existence of the fstream object
{
files[NameText].open(Path, fstream::in | fstream::out);
}
files[NameText] << Line << "\n";
}
See here for why I changed the condition for the while loop.
Your OS may have trouble having that many open files at once. Perhaps you could try something like this.
Alongside your map, keep a list of the names of the files that are open. Each time you need to write to a file, first search for it in your list, remove it and add it to the front of the list. If it's not there, just add it to the front of the list. Check to make sure the file is open. If it's not, then try to open it. If opening it fails, then one by one, remove items from the back of the list, close the corresponding file to that item, and try to open the current file again. Repeat until opening the file succeeds.
Doing this will ensure that the most frequently written to files stay at the front of the list and remain open. The less frequently written files will move to the back, and eventually be closed. The search for the file in the list is not optimal (O(n)), but since we're dealing with writing to files here, which is a much more expensive operation, you shouldn't notice any kind of perf hit.
You are trying to reuse the Text_File fstream. To do this, you have to do a close() to flush the stream, after you are done writing to a csv file. Please see this question: C++ can I reuse fstream to open and write multiple files?
Also: Here's my Google search for this question: http://goo.gl/Oy5KKM
Note that Text_File is a variable and like all variables you can have more than one with the same type. If you need to manage several different files, you can even use std::fstream in any of the standard containers such as std::vector or std::map. Also, you should consider breaking your code down into smaller more manageable parts. For example, you can create a function which takes an std::fstream& as a parameter. This allows the rest of the program to control which std::fstream& is used at any given time. I strongly suggest that you look at different design options to help organize your code.
The existence check statement has no effect - as mentioned already. Perhaps your intention was to do something like this:
if(!CheckExistance(Path.c_str())) {
fstream Text_File;
Text_File.open(Path, fstream::in | fstream::out | fstream::app);
Text_File<<Line<<"\n";
Text_File.close();
}
The fstream within the scope of if statement will hide the one you must have in the outer scope. Also, close is optional - stream will be closed when it goes out of scope.

fstream won't print to file

The following code will print something to a file
std::fstream fout ("D_addr.txt", std::fstream::app);
fout << pkt->Addr() << std::endl;
flush(fout);
fout.close();
While debugging, I watched pkt->Addr() and it has some values. The fout line is passed without problem. Also the file D_addr.txt is created. However after closing the file, the file size is zero! nothing has been written to it.
Where is the problem?
This is not your actual code I guess and if it is I would start with that Addr() function of yours.
Note that fstream::close "closes the file currently associated with the object, disassociating it from the stream. Any pending output sequence is written to the physical file." flush(fout); can be omitted.
You should also specify std::fstream::out flag. "If the function is called with any value in that parameter the default mode is overridden, not combined." So instead of std::fstream::app you should pass std::fstream::app | std::fstream::out.
I'm wondering if you're not using the wrong class. If you want to write to a file, use std::ofstream, and not std::fstream. In particular, the constructor of std::ofstream forces the ios_base::out bit when calling rdbuf()->open; the constructor of std::fstream doesn't (so you're opening the file with neither read nor write access).
And you probably want to check the error status: did the open succeed, and after the close (or the flush), did all of the writes succeed. The usual way of doing this is just:
if ( fout ) {
// All OK...
}
if ( !fout ) {
// Something went wrong.
}
After the open (the constructor), you can use fout.is_open(), which has the advantage of being a little bit more explicit with regards to what you are checking for.
First of all, flush() and fout.close() do not make any harm, but are not needed here, because when fout gets destroyed the file will be closed (and flushed) as part of fstream destructor.
Second, you should use an ofstream or alternatively add the flag std::ios::out to the openmode parameter.
Try something along the lines of:
{
uint64_t x = 42;
std::fstream of("test.txt", std::ios::app);
of << x << std::endl;
}

Fstream fails to create new file

I'm using a FileManager for a project so that reading and writing is less of a hassle for me. Or would be, if I didn't spend all this time debugging it. So, this comfort-class actually caused me stress and time. Awesome.
The problem seems to be the fstream. Before I continue further, here is the structure of my FileManager class.
class FileManager : Utility::Uncopyable
{
public:
FileManager();
void open(std::string const& filename);
void close();
void read(std::string& buffer);
void write(std::string const& data);
private:
std::fstream stream_;
};
Very simple. The buffer is loaded with data during the read function, the data parameter is what's to be written to file. Before reading and writing you must open the file or risk getting a big, fat exception in your face. Kind of like the one I'm getting now.
Scenario: Simple command-line registering of a user, then writing the data to file. I ask for a name and password. The name is copied and appended with .txt (the filename). So it looks like this:
void SessionManager::writeToFile(std::string const& name,
std::string const& password)
{
std::string filename = name + ".txt";
std::string data;
data += name +", " +password;
try
{
fileManager_->open(filename);
fileManager_->write(data);
fileManager_->close();
}
catch(FileException& exception)
{
/* Clean it up. */
std::cerr << exception.what() << "\n";
throw;
}
}
Problem: the open fails. The file is never created, and during the write I get an exception for not having an open file.
FileManager::open() function:
void FileManager::open(std::string const& filename)
{
if(stream_.is_open())
stream_.close();
stream_.open(filename.c_str());
}
and write
void FileManager::write(std::string const& data)
{
if(stream_.is_open())
stream_ << data;
else
throw FileException("Error. No file opened.\n");
}
However, if I create the file beforehand, then it has no troubles opening the file. Yet, when I check, the default std::ios::openmode is std::ios::in | std::ios::out. I can create the file just fine when I only tag std::ios::out, but I want to keep the stream in a read/write state.
How can I accomplish this?
Best method:
void FileManager::open(std::string const& filename)
{
using std::ios_base;
if( stream_.is_open() )
stream_.close();
stream_.open( filename.c_str() ); // ...try existing file
if( !stream_.is_open() ) // ...else, create new file...
stream_.open(filename.c_str(), ios_base::in | ios_base::out | ios_base::trunc);
}
So the code tests for an existing file and, if not, creates it.
You have to call fstream::open with an explicit openmode argument of
ios_base::in | ios_base::out | ios_base::trunc
Otherwise open will fail due to ENOENT.
Table 95 of the draft C++ standard lists possible file open modes and their equivalent in stdio. The default, ios_base::out | ios_base::in is r+. The one I listed above is equivalent to w+. ios_base::out | ios_base::app is equivalent to a. All other combinations involving ios_base::app are invalid.
(At the risk of being scoffed at: you could switch to stdio and use the file mode a+, which reads from the start and appends at the end.)
You cannot use std::ios::in on a non-existing file. Use
std::ios::in | std::ios::out | std::ios::trunc
instead (but make sure it doesn't exist or it will be truncated to zero bytes).
How can I accomplish this?
std::ofstream file("file.txt");
file << data;
Isn't that simpler?
That's the way the library works: std::ios::in | std::ios::out opens it in the equivalent of stdio's "r+", that is it will only open an existing file. I don't believe there's a mode that will do what you are wanting, you'll have to us an OS-specific call or check the file for existence (again, using an OS-specific call) and open it in one mode or the other depending on whether it already exists.
Edit: I assumed that you didn't want the file truncated if it already exists. As other people have noted, if you're happy to truncate any existing file then in | out | trunc is an option.
Just get your function to open
void FileManager::open(std::string const& filename)
{
using std::ios_base;
if(stream_.is_open())
stream_.close();
stream_.open(filename.c_str(), ios_base::in | ios_base::out | ios_base::trunc);
}
if that is the mode you require.
There is no magic way to open a file for read/write creating it if it does not exist but not truncating it (removing its content) if it does. You have to do that in multiple steps.