Error in opening an output stream - c++

In my code I'm opening an output stream and appending the data to the end of the file .. if there is no such file the stream should create one but the problem that it does not.
here is the code snippet:
char output_file[100];
strcpy(output_file, predicate.c_str());
ofstream output_file_ptr1;
output_file_ptr1.open(output_file,ios::out | ios::app | ios::binary );
if(output_file_ptr1.is_open()){
output_file_ptr1 << subject <<" " << object <<"\n";
output_file_ptr1.close();
}
else{
printf("Error opening out file \n");
return -1;
}
subject, object and predicate are variable strings that I created earlier.
Any idea it does not create the file?
+ it is very important for me that the data is appended to the end of the file.
Update:
predicate is the exact file name that I need , but it is not a usual naming i.e
< http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
is an example

A value such as <w3.org/1999/02/22-rdf-syntax-ns#type> is not a valid file name in most environments. Unix-style operating systems (e.g. Linux) do not support "/" inside the file name (unless the directory structure matches).

Related

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.
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
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.

What should binary file look like after conversion from text?

Problem:
Split the binary I/O from the example code into two: one program that converts an ordinary text file into binary and one program that reads binary and converts into text. Test these programs by comparing a text file with what you get by converting it to binary and back.
Example code:
#include "std_lib_facilities.h"
int main(){
cout <<"Please enter input file name.\n";
string name;
cin >> name;
// open file to read, with no byte interpretation
ifstream ifs(name.c_str(), ios_base::binary);
if(!ifs) error("Can't open input file: ", name);
cout << "Please enter output file name.\n";
cin >> name;
// open file to write
ofstream ofs(name.c_str(), ios_base::binary);
if(!ofs) error("Can't open output file: ", name);
vector<int> v;
// read from binary file
int i;
while(ifs.read(as_bytes(i), sizeof(int))) v.push_back(i);
// do something with v
// write to binary file
for(int i = 0; i < v.size(); ++i) ofs.write(as_bytes(v[i]), sizeof(int));
return 0;
}
Here is my code, instead of reading and writing int values, I tried with strings:
#include "std_lib_facilities.h"
void textToBinary(string, string);
//--------------------------------------------------------------------------------
int main(){
const string info("This program converts text to binary files.\n");
cout << info;
const string testFile("test.txt");
const string binaryFile("binary.bin");
textToBinary(testFile, binaryFile);
getchar();
return 0;
}
//--------------------------------------------------------------------------------
void textToBinary(string ftest, string fbinary){
// open text file to read
ifstream ift(ftest);
if(!ift) error("Can't open input file: ", ftest);
// copy contents in vector
vector<string>textFile;
string line;
while (getline(ift,line)) textFile.push_back(line);
// open binary file to write
ofstream fb(fbinary, ios::binary);
if(!fb) error("Can't open output file: ", fbinary);
// convert text to binary, by writing the vector contents
for(size_t i = 0; i < textFile.size(); ++i){ fb.write(textFile[i].c_str(), textFile[i].length()); fb <<'\n';}
cout << "Conversion done!\n";
}
Note:
My text file contains Lorem Ipsum, no digits or special punctuation. After I write the text using binary mode, there is a perfect character interpretation and the source text file looks exactly like the destination. (My attention goes to the fact that when using binary mode and the function write(as_bytes(), sizeof()), the content of the text file is translated perfectly and there are not mistakes.)
Question:
How should the binary file look like after I use binary mode(no char interpretation) and the function write(as_bytes(), sizeof()) when writing?
In both Unix-land and Windows a file is primarily just a sequence of bytes.
With the Windows NTFS file system (which is default) you can have more than one sequence of bytes in the same file, but there is always one main sequence which is the one that ordinary tools see. To ordinary tools every file appears as just a single sequence of bytes.
Text mode and binary mode in C++ concern whether the basic i/o machinery should translate to and from an external convention. In Unix-land there is no difference. In Windows text mode translates newlines from internal single byte C convention (namely ASCII linefeed, '\n'), to external double byte Windows convention (namely ASCII carriage return '\r' + linefeed '\n'), and vice versa. Also, on input in Windows, encountering a single byte value 26, a "control Z", is or can be interpreted as end of file.
Regarding the literal question,
” The question is in what format are they written in the binary file, shouldn't they be written in not-interpreted form, i.e raw bytes?
the text is written as raw bytes in both cases. The difference is only about how newlines are translated to the external convention for newlines. Since your text 1)doesn't contain any newlines, there's no difference. Edit: Not shown in your code except by scrolling it sideways, there's a fb <<'\n' that outputs a newline to the file opened in binary mode, and if this produces the same bytes as in the original text file, then there is no effective translation, which implies you're not doing this in Windows.
About the extra streams for Windows files, they're used e.g. for Windows (file) Explorer's custom file properties, and they're accessible e.g. via a bug in the Windows command interpreter, like this:
C:\my\forums\so\0306>echo This is the main stream >x.txt
C:\my\forums\so\0306>dir | find "x"
04-Jul-15 08:36 PM 26 x.txt
C:\my\forums\so\0306>echo This is a second byte stream, he he >x.txt:2nd
C:\my\forums\so\0306>dir | find "x"
04-Jul-15 08:37 PM 26 x.txt
C:\my\forums\so\0306>type x.txt
This is the main stream
C:\my\forums\so\0306>type x.txt:2nd
The filename, directory name, or volume label syntax is incorrect.
C:\my\forums\so\0306>find /v "" <x.txt:2nd
This is a second byte stream, he he
C:\my\forums\so\0306>_
I just couldn't resist posting an example. :)
1) You state that “My text file contains Lorem Ipsum, no digits or special punctuation”, which indicates no newlines.

Create a file at a given path using C++ in Linux

I want to create a file at a given path that is relative to the current directory. The following code behaves erratically. I some times see the file created and some times do not. That may be because of the change in the current directory. Here's the code.
//for appending timestamp
timeval ts;
gettimeofday(&ts,NULL);
std::string timestamp = boost::lexical_cast<std::string>(ts.tv_sec);
//./folder/inner_folder is an existing directory
std::string filename = "./folder/inner_folder/abc_"+timestamp+ ".csv";
std::ofstream output_file(filename);
output_file << "abc,efg";
output_file.close();
Now, the problem is the file is created only in some cases. That is when I have as a command line argument an input file from the current directory, it works fine.
./program input_file
If I have something like this, it does not work
./program ./folder1/input_file
I tried giving the full path as an argument for ofstream, I still don't see the files created.
What is the correct way to do this? Thanks
ofstream will not create missing directories in the file path, you must ensure the directories exist and if not create them using OS specific api or boost's file system library.
Always check the result of IO operations, and query system error code to determine reason for failures:
if (output_ file.is_open())
{
if (!(output_file << "abc,efg"))
{
// report error.
}
}
else
{
const int last_error = errno;
std::cerr << "failed to open "
<< filename
<< ": "
<< strerror(last_error)
<< std::endl;
}

C++ Binary File method is removing content from the file?

I have an assignment where I am writing input on various things (in the form of structs) and then writing to a binary file. I have to be able to both read and write to the file while the program is open. One of the methods needs to print out all of the clients in the binary file. It seems to be working, except whenever I call that method, it seems to erase the contents of the file and prevent more from being written to it. Here's the applicable snippets:
fstream binaryFile;
binaryFile.open("HomeBuyer", ios::in | ios::app | ios::binary);
The same file is supposed to be usable between times you run the program, so I should open it with ios::app, correct?
Here's the method to add an entry:
void addClient(fstream &binaryFile) {
HomeBuyer newClient; //Struct the data is stored in
// -- Snip -- Just some input statements to get the client details //
binaryFile.seekp(0L, ios::end); //This should sent the write position to the
//end of the file, correct?
binaryFile.write(reinterpret_cast<char *>(&newClient), sizeof(newClient));
cout << "The records have been saved." << endl << endl;
}
And now the method to print all the entries:
void displayAllClients(fstream &binaryFile) {
HomeBuyer printAll;
binaryFile.seekg(0L, ios::beg);
binaryFile.read(reinterpret_cast<char *>(&printAll),sizeof(printAll));
while(!binaryFile.eof()) { //Print all the entries while not at end of file
if(!printAll.deleted) {
// -- Snip -- Just some code to output, this works fine //
}
//Read the next entry
binaryFile.read(reinterpret_cast<char *>(&printAll),sizeof(printAll));
}
cout << "That's all of them!" << endl << endl;
}
If I step through the program, I can input as many clients as I want, and it will output them all the first time I call displayAllClients(). But as soon as I call displayAllClients() once, it seems to clear out the binary file, and any further attempts at displaying clients gives me no results.
Am I using seekp and seekg incorrectly?
From what I understand, this should set my write position to the end of the file:
binaryFile.seekp(0L, ios::end);
And this should set my read position to the beginning:
binaryFile.seekg(0L, ios::beg);
Thanks!
Pasting comment in as this resolved the issue.
You need to call binaryFile.clear() before seekp() and seekg() if EOF is set, otherwise they won't work.
This is the documentation for ios::app
ios::app
All output operations are performed at the end of the file, appending the
content to the current content of the file. This flag can only be used in
streams open for output-only operations.
Since this is homework, I'll let you draw your own conclusions.

Using the filename generated from mkstemp

The mkstemp() function generates a unique temporary filename from template, creates and opens the file, and returns an open file descriptor for the file. The last six characters of template must be "XXXXXX" and these are replaced with a string that makes the filename unique. Since it will be modified, template must not be a string constant, but should be declared as a character array.
After template is replaced with a string that makes the filename unique, I save the string to later use. This is where I'm encountering a strange problem I can't seem to wrap my head around. I can print the correct filename to my terminal, see the file in my file explorer and open it to see the correct contents, but when I include the string as part of a command to execute with popen() I get a pointer to an empty file. When I hard code the names of the tempory files back into my code however and run again, I get the correct result I am expecting. Is there something I'm overlooking or missing? Here is a code snippet:
char tmpname[] = "tmp.XXXXXX";
FILE *fpt = fdopen(mkstemp(tmpname), "w");
string saved_tmpname(tmpname);
// blah
// write to file
// blah blah
const string command = "mycommand " + saved_tmpname;
cout << command << endl; // prints correctly
FILE *fpipe = popen(command.c_str(), "r");
if (fpipe == NULL) {
perror(command.c_str());
}
char buff[4096];
while (fgets(buff, 4096, fpipe)) {
// we don't get here!
}
From the manpage for mkstemp:
The file is opened with the open(2) O_EXCL flag, guaranteeing that the caller is the process that creates the file.
The O_EXCL flag prevents you from opening the file again. This is OK, since it is a temporary file - only one process (the creator) should have access to it. Temporary files contain sensitive data sometimes.