write text file with simultaneous random access and append
The following piece of code tries to overwrite to different locations in a file and also to append to the file. However, it does not seem to work. I have tried several modes and the result is either overwr开发者_如何学JAVAiting or simply appending everything to the end of the file but not both together. Is it possible to do such a thing in C++? Or, is it that we need to open the file again and again in different modes to accomplish this?
int main() {
fstream f("a.dat", ios::out | ios::app);
f.seekp(ios::beg);
f << "hello world";
f.seekp(ios::end);
f << "works";
return 0;
}
Are you looking to insert data somewhere in the file that is not the end? There is no insertion, at least with fstream
s, in my experience. This is much like writing data in an array. Simply having a pointer somewhere between the beginning and end of the array and writing there doesn't insert, it overwrites.
Handle it like you would inserting data into the middle of an array. You have to move data forward to make space for the insertion.
Calculate how much space you'll need for the insertion and read all data after the insertion point to a buffer, the write the data in that buffer back into the file after you've moved your write pointer forward by the amount of space you'll need for the insertion. Then, move your write pointer back to where you wanted to insert and write the data you plan to insert.
- Calculate insertion data length.
- Seek to insertion point.
- Read rest of file to buffer.
- Seek to insertion point.
- Write insertion data.
- Write from rest of file buffer.
EDIT: The requested code example:
#include <fstream>
int main(int argc, char* argv[])
{
std::fstream FileStream;
// Initial write.
FileStream.open("Test.txt", std::ios::out | std::ios::trunc);
FileStream << "OLDDATAOLDDATA";
FileStream.close();
// Prepare data to insert.
const char InsertionData[] = "newdata";
const unsigned int InsertionDataLength = strlen(InsertionData);
// Insertion write.
FileStream.open("Test.txt", std::ios::in | std::ios::out | std::ios::ate);
std::ios::pos_type InsertionPosition = 7; // 7 is the start index of the second "OLDDATA".
FileStream.seekg(0, std::ios::end);
std::ios::pos_type EndPosition = FileStream.tellg();
// Read rest of file.
const unsigned int RestOfFileDataLength = EndPosition - InsertionPosition;
char* const RestOfFileData = new char[RestOfFileDataLength];
FileStream.seekg(InsertionPosition);
FileStream.read(RestOfFileData, RestOfFileDataLength);
// Rewrite rest of file.
FileStream.seekp(InsertionPosition);
FileStream.write(InsertionData, InsertionDataLength);
FileStream.write(RestOfFileData, RestOfFileDataLength);
delete[] RestOfFileData;
FileStream.close();
}
A few notes. The earlier in the file that you insert, the more expensive it is because you need to move everything after it. That also means bigger files are more expensive to insert into. If you're going to do several inserts, you can try to keep track of each insertion and defer them until some kind of flush command is called, at which point only 1 major read/write of the rest of the file is necessary, instead of one for each insertion call. Ideally, you should wrap this up in your own wrapper of some sort.
If you specify ios::app
when you open the file all writes will always go to the end of the file -- it's basically as if every write was preceded by f.seekp(ios::end)
.
It sounds like what you want is ios::ate
instead. This will seek to the end of the file immediately after it's opened, but when/if you seek to somewhere else in the file and write, the write will go to the current point in the file instead of the end.
Edit 3: I've added code to seek back to the end and append more there:
#include <fstream>
#include <iostream>
int main() {
std::fstream f("test.txt", std::ios::in | std::ios::out | std::ios_base::ate);
f << " added to end.";
f.seekp(0, std::ios::beg);
f << "Original";
f.seekp(0, std::ios::end);
f << " Final.";
return 0;
}
Starting with the following as the content of the file "test.txt":
Initial Value.
After running the program, the file should contain:
Original value. added to end. Final.
So, "Added to end." gets, obviously enough, added to the end. We then seek back to the beginning, and overwrite 'Initial" (plus one of the two spaces after it) with "Original".
As an aside, I should add that I was apparently at least half asleep when I posted last night -- I completely missed the fact that you had only supplied on parameter when you called seekp
. With only one parameter, it takes that as the offset into the file. Unfortunately, std::ios::beg
, std::ios::cur
and std::ios::end
are simple integer values, so instead of giving a type error (as you'd really like) it was simply taking the value of those enumerations, and using them as offsets into the file. Purely by luck, std::ios::beg
apparently has the value 0 (at least on my implementation) so it "worked", but only by accident. With two parameters (as the code above has now) the first is the offset, and the second it the reference point (beginning, current position or end) from which that offset starts.
What you'll need to know:
- Append mode writes to the end of the file, no matter what seeking you may do
- Even in other writing modes, writing at the beginning of the file does not prepend, it overwrites. This is not the "fault" of C++ so much as it is the filesystem. It's just the way file manipulation works, and will be the case for just about every platform and every language out there.
- If you really want to insert text in other places, you'll probably need to read the file into memory, manipulate it, then re-write the file to disk. Or in some cases it might make sense to use something like
sed
to manipulate the file.
Languages that provide file preprend capabilities out of the box are rare. I don't know of any that do, unless you count special purpose languages like sed
that aren't good for anything but file manipulation. There may be languages that do what you want in the way you were hoping, but I don't know of any.
精彩评论