C++ ofstream dynamic file names and content - c++

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';

Related

How to logg SRAND outcomes into .txt file in c++?

Hey guys I'm new to this,
I managed to make c++ open a random .jpg file from a folder using srand, the files are named sequentially 1-25.
Now I want to print out which file has been chosen by the randomizer every time I run the programm and log it into a .txt file.
The log in the .txt file should look like this:
4
8
5
..and so on, so that it adds the result of the randomizer to a new line each time it gets executed.
This is the code I have so far:
#include <sstream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <fstream>
using namespace std;
int main()
{
srand((unsigned)time(0));
ostringstream oss;
oss << "\"C:\\Users\\etc..";
oss << rand() % 25 + 1;
oss << ".jpg\"";
system(oss.str().c_str());
system("pause");
return 0;
}
See below a complete example how you can achieve what you described.
The function LogToFile uses std::ofstream to open a file in append mode, and write to it.
You can change it if you'd like a different format (e.g. separate by commas instead of newline).
In order to call it with the number I added a variable n to hold the number (rather than streaming it directly into the std::ostringstream).
A side note: better to avoid using namespace std - see here Why is "using namespace std;" considered bad practice?.
The code:
#include <string>
#include <ctime>
#include <fstream>
#include <sstream>
#include <assert.h>
bool LogToFile(std::string const & filename, int n)
{
std::ofstream file;
file.open(filename, std::ios_base::app); // open in append mode
if (!file.is_open())
{
return false;
}
file << n << std::endl;
file.close();
return true;
}
int main()
{
// change according to your needs:
const std::string LOG_FILENAME = "log.txt";
const std::string IMAGE_FOLDER = "C:\\tmp";
srand((unsigned)time(0));
int n = rand() % 25 + 1;
// Add the number to the log file:
bool bRes = LogToFile(LOG_FILENAME, n);
assert(bRes);
std::ostringstream oss;
oss << "\"" << IMAGE_FOLDER << "\\" << n << ".jpg" << "\"";
system(oss.str().c_str());
system("pause");
return 0;
}

How to write to file outside working directory?

I'm trying to figure out how to write to a file outside the working directory. This is the code I currently have.
#include <iostream>
#include <fstream>
#include <string>
int main()
{
std::string sp{};
std::fstream ss("C:\\Users\\onion\\AppData\\Roaming\\MetaQuotes\\Terminal\\some numbers\\MQL5\\Files\\testnew.txt", std::ios::in | std::ios::out);
if (!ss.is_open()) std::cout << "Failed" << '\n';
else
{
while (ss.is_open())
{
std::getline(ss, sp);
std::cout << sp << '\n';
ss << "new data";
if (ss.eof())break;
}
}
}
I can read the file perfectly fine, but I cant write to it? Could it be that Metatrader itself is limiting my ability to write to a file or does a file have to be in the working directory to be able to write to it? or am I just doing it wrong?

Standard functions and operations not working in class constructor

I am trying to make my first class with a constructor and it seems to be acting strangely.
My class is derived from filebuf and for some reason, I am unable to open it in the constructor.
I tried to add a cout statement for debugging, but the << operator is not recognized.
#include <iostream>
#include "bin.h"
int main()
{
bin myBin("e:\Temp\test.txt");
system("PAUSE");
return 0;
}
bin.h
#pragma once
#include <fstream>
#include <cstdlib>
#include <cstring>
class bin : private std::filebuf {
int buffSize = 1000;
char* buffer;
unsigned int length;
short int buffCounter;
public:
bin(std::string fileName)
{
open(fileName.c_str(), std::ios::in | std::ios::out | std::ios::trunc);
if (!is_open())
std::cout << "ERROR: failed to open file " << fileName << std::endl;
//set all IO operations to be unbufferred, buffering will be managed manually
setbuf(0, 0);
//create buffer
buffer = new char[buffSize];
};
virtual ~bin()
{
delete buffer;
};
};
bin myBin("e:\Temp\test.txt");
You have to correct above line as follows:
bin myBin("e:\\Temp\\test.txt");
DEMO: http://cpp.sh/7b4k
It looks like you need:
#include <iostream>
To use std::string you need:
#include <string>
The iostream include may have forward-declared std::string but without the full definition you don't get operator<< (or c_str()).
Some other answerers may be unable to reproduce your problem because different standard libraries might have their iostream fully do #include <string> (this is permitted but not required).
std::cout << "ERROR: failed to open file " << fileName << std::endl;
Should be
std::cout << "ERROR: failed to open file " << fileName.c_str() << std::endl;
std::cout doesn't always accept std::string but does accept const char *

How to truncate a file while it is open with fstream

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.

output list of files from popen to ifstream.open

Basically I need to open and read a list of files I get from another command.
For each line of output of popen
open a file usen ifstream.open
it compiles and if I put the file name directly it works fine, but it doesn't do anything when using popen output. I've seen questions like this but none of this particular way of giving filenames.
here's the code:
#include <iostream>
#include <sqlite3.h>
#include <stdio.h>
#include <fstream>
using namespace std;
int main () {
ifstream singlefile;
FILE *filelist;
char filename[512];
string progline;
if(!(filelist = popen("find `pwd` -name \"*.js\"", "r"))){
return 1;
}
while( fgets(filename, sizeof(filename), filelist)!=NULL)
{
cout << filename;
singlefile.open(filename, ifstream::in);
while ( singlefile.good() )
{
getline (singlefile,progline);
cout << progline << endl;
}
singlefile.close();
}
pclose(filelist);
return 0;
}
next step would be not open each file inside the loop but to store the file list and then open each file.
Thanks
fgets keeps the trailing newline, resulting in a filename of a non-existing file. Also the stream state is only updated after reading. If I replace the while body with the following code, it works for me:
cout << filename;
size_t len = strlen(filename);
// chop off trailing newline
if (len > 1 && filename[len - 1] == '\n') filename[len - 1] = 0;
singlefile.open(filename, ifstream::in);
while ( getline(singlefile, progline) )
{
cout << progline << endl;
}
singlefile.close();
If you actually want to iterate through a list of files, I'd use Boost.Filesystem, which has a nice C++ interface, works for all filenames (even for those with newlines), and is platform-independent.
If this actually is only an example and your actual command is not find, there is still some room for simplification. Here is a suggestion that uses Boost.Iostreams to get rid of most of the C function calls (it would be great to have a device source reading from a process's standard output, but Boost.Iostreams lacks that):
#include <cstdio>
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <string>
#include <stdio.h>
#include <boost/noncopyable.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
using namespace std;
namespace io = boost::iostreams;
class Popen: private boost::noncopyable {
public:
explicit Popen(const char* command):
m_stream(popen(command, "r")) {
if (!m_stream) throw runtime_error("popen failed");
}
~Popen() {
pclose(m_stream);
}
FILE* stream() const {
return m_stream;
}
private:
FILE* m_stream;
};
int main() {
Popen pipe_wrapper("find `pwd` -name \"*.cpp\"");
io::file_descriptor_source pipe_device(fileno(pipe_wrapper.stream()), io::never_close_handle);
io::stream<io::file_descriptor_source> pipe_stream(pipe_device, 0x1000, 0x1000);
string filename;
while (getline(pipe_stream, filename)) {
cout << filename << endl;
ifstream file_stream(filename.c_str(), ifstream::in);
string progline;
while (getline(file_stream, progline)) {
cout << progline << endl;
}
}
}