c++ ofstream write_to_log.open (" relative path + array"); - c++

I want the line below to write a new file using the content given in the array
but into a new folder named logs:
char log_file_name[100]; /* this array contains the name of a new file */
ofstream write_to_log;
write_to_log.open (relative path, log_file_name , fstream::app);
How do I get it working ?

You can use CreateDirectory for creating folders with VC++ in Windows.
#include <windows.h>
#include <string>
#include <fstream>
using namespace std;
int main() {
string path = "C:\\users\\folder";
CreateDirectory(path.c_str(), NULL);
char log_file_name[100] = "log.txt";
path += '\\';
path += log_file_name;
ofstream write_to_log(path.c_str(), fstream::app);
return 0;
}
The NULL refers to a security attributes structure that you may have to create. More details at MSDN here and in this answer.

You can save your self a lot of potential trouble and replace char log_file_name[100]; with std::string log_file_name; The benefits of string are many, the most important here are they resize and they make appending really easy. The string does everything a char array does and a whole lot of extras. In virtually all cases, you should chose a string over a char array.
string path;
string log_file_name;
With the path and the file name as strings
path += "\\" + log_file_name
ofstream write_to_log(path, fstream::app);
if (write_to_log)
{ // file is open and looks writable (have to start writing to be sure)
// do stuff. Or not. It's a free country.
}
else
{ // file didn't open
// Handle error
}
All done and the file, if it exists and is writable, is open and ready to go. Always check the state of a stream when you use it. SO is littered with questions from people who didn't and got confused by the result.
On older compilers you may have to change the create and open line slightly:
ofstream write_to_log(path.c_str(), fstream::app);

Related

Ofstream doesnt work after I tried opening it with an UTF-16- string (C++)

My program opens a *.txt in an std::basic_ofstream<char16_t> and writes UTF-16 characters in it.
Opening a file with ASCII characters as name works flawlessly.
The problem I face is the following:
Sometimes, the name of the file (given by the program, by design) contains non- ASCII characters. Since I can't open a file named with UTF-16 characters (because that would require a deep understanding of the topic, afaik), I try to filter that name out.
I do that by first opening the file with the std::basic_ofstream<char16_t>, look if it didn't work, and then open it with a "default" name, which consists only of ASCII characters.
Unfortunately, since the filestream is the same, I can't open the file with the default name anymore after I tried to open it with the ASCII- characters.
Here's a minimal code which illustrates my problem.
#include "vector"
#include "cstring"
#include "string.h"
#include "sstream"
#include "iostream"
#include "fstream"
#include "locale.h" // Unicode
#include "codecvt" // Unicode
typedef std::basic_string<char16_t> u16string;
int main(int argc, char *argv[])
{
std::basic_ofstream<char16_t> outputStream;
std::locale loc(std::locale(), new std::codecvt_utf16<wchar_t>);
outputStream.imbue(loc);
u16string currentPath = utf8_to_utf16("C:\\OutputDir\\");
u16string endPath;
u16string defaultName = utf8_to_utf16("Default");
u16string customName = utf8_to_utf16("Jörg"); // Could be anything, e.g. Jörg, Bernard, Sean
endPath += currentPath; // Add path
endPath += utf8_to_utf16("/");
endPath += customName; // Add name
endPath += utf8_to_utf16(".txt"); // File extension
// Try to open file
outputStream.open(utf16_to_utf8(endPath), std::ios::out);
if(outputStream.good() && outputStream.is_open())
{
// Was able to open (because no utf16 chars were in the name)
outputStream << utf8_to_utf16("This wörked.");
outputStream.close();
return 0;
}
else
{
// Wasnt able to open etc, so the name contains non- ascii characters. Need to open with default name.
endPath.clear();
endPath += currentPath; // Add path
endPath += utf8_to_utf16("/");
endPath += defaultName; // Add default name
endPath += utf8_to_utf16(".txt"); // File extension
outputStream.open(utf16_to_utf8(endPath), std::ios::out); // This doesnt work... Why?
if(outputStream.good() && outputStream.is_open())
{
// Was able to open (because no utf16 chars were in the name)
outputStream << utf8_to_utf16("This wörked.");
outputStream.close();
}
}
return 0;
}
So basically it's like this:
Name contains no utf16 chars -> Everything works fine.
Name contains utf16 chars -> Doesnt open. Okay, alright. Then now please open with the default name, which does not contain utf16 chars -> wont do it.
If I open the file with an ASCII name in the first place, everything's okay.
But when I try to open with utf16 chars first (so it deliberately fails, because I want to sort out utf16 names) and then try to open with the default, it wouldn't work.
I hope you can understand my problem. Basically, I want to filter UTF-16 names and I do that by checking whether the file was opened successfully or not.
Can anyone help me? Or maybe someone has a more elegant solution. I'm open to it.
Btw dont mind the utf16_to_utf8() and utf8_to_utf16() functions. They do work perfectly fine and are not the problem. If you want some source code one them, heres my Qt misuse:
std::basic_string<char16_t> MainWindow::utf8_to_utf16(std::string string)
{
return QString::fromStdString(string).toStdU16String();
}
std::string utf16_to_utf8(std::basic_string<char16_t> string)
{
return QString::fromStdU16String(string).toStdString();
}
Maybe there's a little error in them but as far as I use them they do work fine.

I want to create a text file in cpp using ofstream

I want to create a file qbc.txt. I know how to create it, but I want to create a program that, if a file already exists with the same name, it would rename it to qbc(1).txt.
In C++17, boost's filesystem library was standardized as std::filesystem
It comes with a convenient std::filesystem::exists function.
It accepts a std::filesystem::path object, but fortunately those can be constructed with a std::string, making our program trivially easy:
std::string prefix = "qbc";
std::string extension = ".txt";
std::filesystem::path filename{prefix + extension};
int i = 0;
while (std::filesystem::exists(filename)){
filename = prefix + "(" + std::to_string(++i) + ")" + extension;
}
// now filename is "qbc(1)" or "qbc(2)" etc.
Unfortunately no compiler has full support for it at the time of this writing!
Here is a simple solution. The file_exists() function came from #Raviprakash in his response. I've added how to change the filename and try again until success. I've done an approach similar to this before in Python.
If you know that your program is the only one that will create or remove these files, then you can cache the last created one and simply create the next one instead of looping over all of the created ones every time. But this kind of optimization would only make sense if you plan to make hundreds of thousands of files this way.
#include <fstream>
#include <string>
bool file_exists(const std::string &filename) {
std::ifstream in(filename);
return in.good();
}
std::ofstream& open_new(std::ofstream &out, std::string prefix,
std::string suffix)
{
std::string filename = prefix + suffix;
unsigned int index = 0;
while (file_exists(filename)) {
index++;
filename = prefix + "(" + std::to_string(index) + ")" + suffix;
}
out.rdbuf()->open(filename, std::ios_base::out);
return out;
}
int main() {
std::string prefix = "qbc";
std::string suffix = ".txt";
std::ofstream out;
open_new(out, prefix, suffix);
out << "hello world!\n";
return 0;
}
I know the program needs some improvements but the general idea is here:
#include <fstream>
#include <string>
using namespace std;
inline bool file_exists(const std::string& name)
{
ifstream f(name.c_str());
return f.good();
}
int main()
{
string filename, name;
name = "qbc";
filename = name;
int counter = 1;
while (file_exists(filename+".txt")) {
string str = to_string(counter);
filename = name+ "(" + str + ")";
counter++;
}
filename += ".txt";
ofstream out(filename.c_str());
return 0;
}
I don't think this can be entirely solved using just the standard libraries. You can certainly keep picking a new file name until you find one that's unused and then create the new file (as the other answers have shown).
But there's an inherent race condition in that approach. What if another process creates a file between the time your program decides the name is available and the time it actually creates the file? Imagine two copies of your program both trying to write out files.
What you need is an atomic way to check for the file's existence and also to create the file. The normal way to do that is to first just try to create the file and then see if you succeeded or not. Unfortunately, I don't think the standard C++ or C libraries give you enough tools to do that. (I'd be happy to be proven wrong about that.)
Operating systems often provide APIs for doing just that. For example, Windows has GetTempFileName, which just keeps trying to create a new file until it succeeds. The key is that, once it succeeds, it keeps the file open so that it knows no other process can steal the name that's been selected.
If you tell us which OS you're using, we might be able to provide a more detailed answer.

Input Output with fstream

Can anyone tell me what is wrong with this code? I always get not open.
#include <iostream>
#include <fstream>
using namespace std;
int main(){
fstream fs;
fs.open("fsfile2",ios::in|ios::out|ios::binary);
if(fs.is_open()){
fs.write("wow",sizeof("wow"));
char str[20];
fs.read((char*)str,sizeof(str));
cout<<str<<endl;}
else
cout<<"Not open\n";
return 0;
}
Try this code
fs.open("fsfile2", ios::app|ios::in|ios::out|ios::binary);
By using the open() like you are that file will not be created if that is your goal.
If you want to create a new file please look at: fstream won't create a file
If the file exists, you are not looking for it in the right path. Or change the file name to the full path or put the executable in the folder where the file is.
Hope this helps.
Probably, you do not have permissions to create files in the directory, where your executable is.
Solution:
Please add a file extension to the filename.
If it's a text file, it will be
"fsfile2.txt"
Then, I tried removing
ios::in
since the first process only writes to file, and by removing that, the file is created and "wow" is also written at it.
In order for these lines
fs.read((char*)str,sizeof(str));
cout<<str<<endl;
to work,
You need to close the stream after writing to it, then open the stream in read mode, then read the contents. Take note that closing the stream will save the edited file.
Additional:
You can also change
fs.write("wow",sizeof("wow"));
to
fs << "wow";
You can do the same when reading from file,
fs >> str;
You can also use the string class of C++, instead of char array so that the number of characters inside the file won't be your problem anymore.
#include <string>
string str;
Checking for EOF (end-of-file) is recommended since files are read line by line. Once you add a new line and add a character to the line, the code that doesn't loop until EOF will only read the first line of the file.
In order to solve this, it is recommended to loop until EOF is reached.
while(!fs.eof()) {
fs >> str;
cout << str << endl;
}
So here is the improved snippet:
#include <string>
fs.open("fsfile2.txt", ios::out); // ios::out for write only
if(fs.is_open()) {
// writes "wow" to file
fs << "wow";
// closes the file
fs.close();
// ios::in for read only
fs.open("fsfile2.txt", ios::in);
// better to define variable just before using it
string str;
// loops until end-of-file
while(!fs.eof()) {
// reads a line from file, stores it to str
fs >> str;
// shows str to screen
cout << str << endl;
}
}
*Note: I removed
ios::binary
Since your code is not dealing with binary files yet.
I tried these and it worked fine! Have a nice day!
fstream fs; does not create a new file for you.
You need to make sure that the file exists in your project directory.
On the other hand, if you were to use ofstream fs("file.txt"); it would create the file for you. Or use only ios::out when you open fstream fs, this will create the file for you.

Accessing data from file

I'm aware this has been discussed a lot on SO (and elsewhere). I'm resorting to asking here as I'm still stuck. Maybe I'm doing something really brainless/silly or maybe it's a genuine gotcha...
I have a directory of files, each with the extension '.pts' - opening one reveals:
version: 1
n_points: 68
{
498.801220 504.771171
516.076459 571.681686
518.038170 628.516761
...
Pasting here gets the formatting wrong a little, so be advised that there is no empty line between each line of data. There are also 68 of these float pairs, however I omit them here.
Eventually I want a vector of the pairs as rounded shorts - e.g. 499, 505, 516, 572, 518, 629 ...
But at the moment I can't access the floats as is, let alone any information.
The code so far:
#include <iostream>
#include <string>
#include "dirent.h"
#include <fstream>
#include <sstream>
#include <stdio.h>
using namespace std;
bool has_suffix(const string& s, const string& suffix)
{
return (s.size() >= suffix.size()) && equal(suffix.rbegin(), suffix.rend(), s.rbegin());
}
int main(int argc, char **argv)
{
string path = "C:\\testset";
DIR *dir = opendir(path.c_str());
if(!dir)
{
return 1;
}
dirent *entry;
string fileName;
float number;
string dummy;
while(entry = readdir(dir))
{
if(has_suffix(entry->d_name, ".pts"))
{
fileName = entry->d_name;
fileName = path + "\\" + fileName; // <<added at suggestion of Martin James
//Working up to here as I see all the .pts files listed when I print them:
cout << fileName << endl;
ifstream file(fileName, std::ios_base::in);
//however from here:
while (file >> number)
{
//...nothing will print
printf("%f ", number);
}
file.close();
}
}
closedir(dir);
}
Finding all the .pts files is working (thanks other thread on SO :) - and they do appear to be simple .text files as they open in text editors. There is a 'LF' at the end of each line.
Problem is that running the code will only result in the file names being listed. It seems that "while (file >> number)" isn't returning anything to run the print statement. Thing is, other threads on the topic indicate (at least to me) that it is as simple as this.
I feel it's likely that the answer will involve things I've looked at in the last few hours - dummy string variables to soak up all the stuff at the top of the file I don't need? getline? 'tokens'? I would show you all my attempts at nutting it out but I don't want to bog down the question:
Given a txt file formatted like above how would you access the values from line 4 onwards?
Any help greatly appreciated :)
The problem here is that, you are not reading in the header information. The >> operation fails, because you are trying to read in the string "version: 1" as a float.
To fix this, you should either skip the header with few calls to std::getline or parse it to get information about the contents.
Edit: To skip the lines you can just do:
for(int i=0;i<3;i++) //Skip header
std::getline(file,dummy);

New to <dirent.h>, trying to access data in a directory

I've never used dirent.h before. I was using istringstream to read through text files (singular), but have needed to try to revise the program to read in multiple text files in a directory. This is where I tried implementing dirent, but it's not working.
Maybe I can't use it with the stringstream? Please advise.
I've taken out the fluffy stuff that I'm doing with the words for readability. This was working perfectly for one file, until I added the dirent.h stuff.
#include <cstdlib>
#include <iostream>
#include <string>
#include <sstream> // for istringstream
#include <fstream>
#include <stdio.h>
#include <dirent.h>
void main(){
string fileName;
istringstream strLine;
const string Punctuation = "-,.;:?\"'!##$%^&*[]{}|";
const char *commonWords[] = {"AND","IS","OR","ARE","THE","A","AN",""};
string line, word;
int currentLine = 0;
int hashValue = 0;
//// these variables were added to new code //////
struct dirent *pent = NULL;
DIR *pdir = NULL; // pointer to the directory
pdir = opendir("documents");
//////////////////////////////////////////////////
while(pent = readdir(pdir)){
// read in values line by line, then word by word
while(getline(cin,line)){
++currentLine;
strLine.clear();
strLine.str(line);
while(strLine >> word){
// insert the words into a table
}
} // end getline
//print the words in the table
closedir(pdir);
}
You should be using int main() and not void main().
You should be error checking the call to opendir().
You will need to open a file instead of using cin to read the contents of the file. And, of course, you will need to ensure that it is closed appropriately (which might be by doing nothing and letting a destructor do its stuff).
Note that the file name will be a combination of the directory name ("documents") and the file name returned by readdir().
Note too that you should probably check for directories (or, at least, for "." and "..", the current and parent directories).
The book "Ruminations on C++" by Andrew Koenig and Barbara Moo has a chapter that discusses how to wrap the opendir() family of functions in C++ to make them behave better for a C++ program.
Heather asks:
What do I put in getline() instead of cin?
The code at the moment reads from standard input, aka cin at the moment. That means that if you launch your program with ./a.out < program.cpp, it will read your program.cpp file, regardless of what it finds in the directory. So, you need to create a new input file stream based on the file you've found with readdir():
while (pent = readdir(pdir))
{
...create name from "documents" and pent->d_name
...check that name is not a directory
...open the file for reading (only) and check that it succeeded
...use a variable such as fin for the file stream
// read in values line by line, then word by word
while (getline(fin, line))
{
...processing of lines as before...
}
}
You probably can get away with just opening the directories since the first read operation (via getline()) will fail (but you should probably arrange to skip the . and .. directory entries based on their name). If fin is a local variable in the loop, then when the outer loop cycles around, fin will be destroyed, which should close the file.