std::ofstream properly seek the file and add element into it

I am trying to add elements into a .json file between [] as last.

How can I move the cursor to add elements between [...] with efficiently with std::ofstream?

I have tried several open modes but there are strange things. First I created this question about not able to use the file streaming for read and write because of the overwrite issue.

 #include <iostream> #include <fstream> int main () { char errmsg[2048]; std::ofstream ostream; ostream.exceptions(std::ios_base::badbit); try { ostream.open("LS22731.json", std::fstream::ate | std::fstream::in); strerror_s(errmsg, 2048, errno); std::cout << "Error (" << errno << "): " << errmsg << std::endl; if (ostream && ostream.is_open()) { auto ppos = ostream.tellp(); std::streampos sub = 1; // std::cout << "Tellp: " << ppos << std::endl; // Always show zero but file has large data if (ppos > 1) ostream.seekp(ppos - sub) << "aa"; ppos = ostream.teelp(); std::cout << "New tellp: " << ppos << std::endl; ostream.close(); } } catch (std::ios_base::failure& fb) { std::cout << "Failure: " << fb.what() << std::endl; char errmsg[2048]; strerror_s(errmsg, 2048, errno); std::cout << "Error (" << errno << "): " << errno << std::endl; } } 

I searched about open modes then I found this but is it good to open file with both mode std::fstream::ate | std::fstream::in together for std::ofstream? And when I open the file with std::fstream::out mode it is rewriting so deleting whole document,

  • std::fstream::out: Delete all contents of the file (overwrite)
  • std::fstream::app: Cannot move the cursor with seekp
  • std::fstream::ate: Delete all contents of the file (overwrite)
  • std::fstream::binary: Delete all contents of the file (overwrite)
  • std::fstream::ate | std::fstream::app: Cannot move the cursor with seekp
  • std::fstream::ate | std::fstream::out: Delete all contents of the file (overwrite)
  • std::fstream::ate | std::fstream::in: Can move the cursor but not insert delete all after.

I don't want to use c FILE.

7

2 Answers

Well JSON files are err... sequential text files. That means that the file contains a stream of bytes representing the JSON content. And AFAIK, no filesystem has provision for inserting data in the middle of a sequential file. The foolproof way is:

  • copy up to the insertion point to a temp file
  • write the new data
  • add the remaining data from the original file
  • rename the old file to a backup name
  • rename the temp file with the original name
  • (optionaly) remove the backup file

The brave way is to move the second part up by chunks starting from the end to create an emply place to put the data write the new data in that place, and pray all along the operation for no problem in the middle because the file would be irremediably corrupted.

Those 2 ways can process files of arbitrary sizes. For small files, you could load everything in memory, write the new data at the insertion point and rewrite the remaining data after the new data. You just need to use a default fstream and use neither ate nor trunc. out does not mean deleting all the file content. You simply replace the original bytes at the place where you write.

So you should use:

ostream.open("LS22731.json", std::fstream::out | std::fstream::in); 

Then you:

  • read up to your insertion point and discard the data
  • note the position with tellp
  • read the end of file and save it
  • go to the insertion point
  • write the new data
  • write the saved data
  • close the stream

Here is an adaptation of the previous algorithm. The cautious points as:

  • you must use a fstream with std::fstream::out | std::fstream::in mode to be able to read and write a file. The file must exist and you will be initially positioned at the beginning of the file
  • to reliably be able to compute positions, you must open the file in binary mode (std::fstream::binary)(should be possible in text mode but I could not find my way...)

Here is a close adaptation of your code: it opens the file, search for the first closing bracket (]), and inserts ,"h" before to simulate adding a value into a list.

... std::fstream ostream; ostream.exceptions(std::ios_base::badbit); try { // use binary mode to ba able to relyably seek the file. ostream.open("LS22731.json", std::fstream::out | std::fstream::in | std::fstream::binary); strerror_s(errmsg, 2048, errno); std::cout << "Error (" << errno << "): " << errmsg << std::endl; if (ostream && ostream.is_open()) { std::streampos ppos; // search the first ] ostream.ignore(std::numeric_limits<std::streamsize>::max(), ']'); // we want to insert just before it ppos = ostream.tellg() - std::streampos(1); ostream.seekg(ppos); // prepare to read from the ] std::string old = "", tmp; // save end of file, starting at the ] while (std::getline(ostream, tmp)) { old += tmp + "\n"; } ostream.clear(); // clear eof indicator ostream.seekp(ppos, std::ios::beg); // go back to the insertion point ostream << ",\"h\""; // add some data ostream << old; // add the remaining of the original data ostream.close(); } ... 

Disclaimers:

  • DO NOT PRETEND I ADSISED YOU THIS WAY. If there is a problem in the middle of processing, the file will be irremediately corrupted.
  • it will fail miserabily if a text field contains a closing bracket, because it is not a JSON parser
11

If you open a file for reading, you cant set the write head of it.

You are using std::ofstream with ios::in mode which I'm not sure is effective. but std::ofstream must be opened with ios::out or ios::app. When you override the default you should give also the default.

If you need to open a file for both read and write, you should use std::fstream.

Another issue is that you trying to add some string in the middle of a text file, and it is not so good idea, it is not similar to paste some string in a text file when opened in Notepad. you must replace a section with another section with the same length, pushing some string won't move the rest of the data forward.

I think the easy way is to read the whole JSON to memory, process it by add or remove some data, and finally rewrite the whole JSON to the file.

1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like