How to convert Qt file path from resource to absolute path? - c++

I need to receive the absolute file path like
C:/Users/Dima/YandexDisk/_Application/WeldAnalysis/ExperimentDefaults.xlsx
from path of QRC file like
:/Data/ExperimentDefaults.xlsx.
How can I do this?

Resources are packed all together into the QRC file, they are not exposed to the filesystem, thus they cannot be converted to a standard path; it is like accessing a file inside a zip file.
What are you willing to do with such file path? QFile can deal with resource paths (":/...") and open such files, but other file handling APIs don't (such as C's fopen or C++'s std::ifstream). Also, and for the same reason, non-Qt application won't be able to open such files if passed as a parameter.
Options you have include:
opening the file using QFile and read it using related Qt classes (given you are dealing with an Excel file I think it doesn't worth the effort, unless you have some library to read it from a memory buffer).
copying the file out of the resources container to a temporary directory (you can use QTemporaryDir to create one), and use the file from there:
QTemporaryDir tempDir;
if (tempDir.isValid()) {
const QString tempFile = tempDir.path() + "/yourfile.xlsx";
if (QFile::copy(":/yourfile.xlsx", tempFile)) {
// your file is now extracted to the filesystem
}
}
Only be aware that when tempDir is destroyed, the temporary directory is also deleted, therefore your file. You can avoid this by disabling the auto-remove feature: tempDir.setAutoRemove(false) and manually deleting the temporary directory when you finish with the extracted file.

Related

C++ how to copy a FILE * pointer

I found a solution here Duplicating file pointers?
FILE *fp2 = fdopen (dup (fileno (fp)), "r");
but according to http://man7.org/linux/man-pages/man2/dup.2.html,
the new file descriptor created by dup, they refer to the same open file descriptor, and thus share status. That's not what I want. I want to create a totally new IO object which refers to the file pointed by the old FILE *
Is there any way to do this?
Add:
I don't have the filename actually. I'm doing a deep copy of an object, which hold an open FILE pointer, so I have to copy that also.
I want to create a totally new IO object which refers to the file pointed by the old FILE *
You're assuming that the file associated with the original FILE * has some form of identity distinct from the IO object by which it is accessed. That is true for regular files and some other objects, but false for others, such as sockets and pipes. Thus, there is no general-purpose mechanism for doing what you ask.
For the special case of objects that can be accessed via the file system, the way to create a new IO object associated with the same file is to open() or fopen() the file via a path to it. That's what these functions do. There is no standard way to get a path from a FILE * or file descriptor number, but on Linux (since you tagged that) you can use readlink() on the open file's entry in /proc, as described here.
Do be aware that even for regular files, the readlink approach is not guaranteed to work. In particular, it will not work if the path with which the original file was opened has since been unlinked, and in fact, in that case it could lead to the wrong file being opened instead. You can check for that by running fstat() on both the old and new file descriptor numbers -- if the files are in fact the same, then they will have the same inode numbers on the same host device.

QT resources doesn't work

I'm writing a program that is supposed to work on the other computer, so I need to have my .txt files with it.
I have created a QT resource file and added one .txt file, then I used it in three windows. It worked perfectly fine until I added another .txt file to resource that is being used in only one window.
So I have users.txt file from which I am logging to program and managing users accounts and I have cars.txt which is a database for cars.
QFile text(":konta/users.txt");
text.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream wczytaj(&text);
This part works but in the next window
QFile text(":konta/users.txt");
text.open(QIODevice::ReadOnly);
QTextStream wczytaj(&text);
This part doesn't work, I have function which checks if file is open and if it exists, and they returns that it exists but is not open.
and now the newest window:
QFile baza(":/auta/cars.txt");
baza.open(QIODevice::Append | QIODevice::Text);
QTextStream zapisz(&baza);
if(!baza.exists()){QMessageBox::warning(this,"Uwaga","Baza nie została odnaleziona");}
else if(!baza.isOpen()){QMessageBox::warning(this,"Uwaga","Baza nie została otworzona");}
Same issue here, the file exists but is not opened. It's weird that it works in one window. Before I added the second .txt file, all the cases worked (except the third part of the code which didn't exist at that time). I didn't change anything in those files or functions.
I should also add that if I give them full path to the file C/folder/folder/file.txt, it works with no problems, but I need it to work on other computers as well, so it's not good for me.
I suppose that problem may be with currents path. When you start your application from Qt Designer current path is ../ (parent of the current directory). When your start your application directly default path is ./ (current directory where your .exe is). You may use this code to get universal path to default directory:
QString path = QApplication::applicationDirPath() % "/";
By the way you may use QFileDialog class to let you user choose files manually. And it is better to use SqLite for storing you users, cars etc. than text files. Qt provides in-built support for SqLite - it it fast and easy to use.

C++ fstream open

I am able to create the files such as
f.open("file")
f.open("./path/file")
f.open("../path/file")
but not
f.open("~/path...)
f.open("/path...)
How do I get the absolute paths to work?
By default, std::fstream::open(filename)
opens filename for both input and output. Hence that file must exist and you must
have write permission to it.
In your cases:
f.open("file")
f.open("./path/file")
f.open("../path/file")
you were lucky.
In your case:
f.open("~/path...")
you used the path-element ~, which means $HOME in the shell but just
means ~ in C++.
In the case:
f.open("/path...")
you were unlucky: either the file didn't exist or you didn't have write permission.
If you want to open a file simply for input then either:
use std::ifstream
use std::fstream f; f.open(filename,std::ios_base::in);
If you want to open a file simply for output then either:
use std::ofstream
use std::fstream f; f.open(filename,std::ios_base::out);
With f.open("~/path/....") it is necessary for you to expand the ~ in code. This is typically done by calling getenv("HOME") to retrieve the home directory, and replacing every occurence of '~' in the path with the home directory.
When working with absolute paths, like "/path/whatever", you need to check that f.open() succeeds. There are various reasons that f.open() might fail, including access control (protections that prevent opening a file), the file already being opened by another process, the directory does not exist, etc.
Notably, f.open(), when attempting to open a file within a directory, requires that all directories in the path already exist. It won't exercise some magic to make them exist. If you want that, you need to code it. Bear in mind that each phase (creating directories, etc) might fail.

Write a file in a specific path in C++

I have this code that writes successfully a file:
ofstream outfile (path);
outfile.write(buffer,size);
outfile.flush();
outfile.close();
buffer and size are ok in the rest of code.
How is possible put the file in a specific path?
Specify the full path in the constructor of the stream, this can be an absolute path or a relative path. (relative to where the program is run from)
The streams destructor closes the file for you at the end of the function where the object was created(since ofstream is a class).
Explicit closes are a good practice when you want to reuse the same file descriptor for another file. If this is not needed, you can let the destructor do it's job.
#include <fstream>
#include <string>
int main()
{
const char *path="/home/user/file.txt";
std::ofstream file(path); //open in constructor
std::string data("data to write to file");
file << data;
}//file destructor
Note you can use std::string in the file constructor in C++11 and is preferred to a const char* in most cases.
Rationale for posting another answer
I'm posting because none of the other answers cover the problem space.
The answer to your question depends on how you get the path. If you are building the path entirely within your application then see the answer from #James Kanze. However, if you are reading the path or components of the path from the environment in which your program is running (e.g. environment variable, command-line, config files etc..) then the solution is different. In order to understand why, we need to define what a path is.
Quick overview of paths
On the operating systems (that I am aware of), a path is a string which conforms to a mini-language specified by the operating-system and file-system (system for short). Paths can be supplied to IO functions on a given system in order to access some resource. For example here are some paths that you might encounter on Windows:
\file.txt
\\bob\admin$\file.txt
C:..\file.txt
\\?\C:\file.txt
.././file.txt
\\.\PhysicalDisk1\bob.txt
\\;WebDavRedirector\bob.com\xyz
C:\PROGRA~1\bob.txt
.\A:B
Solving the problem via path manipulation
Imagine the following scenario: your program supports a command line argument, --output-path=<path>, which allows users to supply a path into which your program should create output files. A solution for creating files in the specified directory would be:
Parse the user specified path based on the mini-language for the system you are operating in.
Build a new path in the mini-language which specifies the correct location to write the file using the filename and the information you parsed in step 1.
Open the file using the path generated in step 2.
An example of doing this:
On Linux, say the user has specified --output-path=/dir1/dir2
Parse this mini-language:
/dir1/dir2
--> "/" root
--> "dir1" directory under root
--> "/" path seperator
--> "dir2" directory under dir1
Then when we want to output a file in the specified directory we build a new path. For example, if we want to output a file called bob.txt, we can build the following path:
/dir1/dir2/bob.txt
--> "/" root
--> "dir1" directory under root
--> "/" path separator
--> "dir2" directory under dir1
--> "/" path seperator
--> "bob.txt" file in directory dir2
We can then use this new path to create the file.
In general it is impossible to implement this solution fully. Even if you could write code that could successfully decode all path mini-languages in existence and correctly represent the information about each system so that a new path could be built correctly - in the future your program may be built or run on new systems which have new path mini-languages that your program cannot handle. Therefore, we need to use a careful strategy for managing paths.
Path handling strategies
1. Avoid path manipulation entirely
Do not attempt to manipulate paths that are input to your program. You should pass these strings directly to api functions that can handle them correctly. This means that you need to use OS specific api's directly avoiding the C++ file IO abstractions (or you need to be absolutely sure how these abstractions are implemented on each OS). Make sure to design the interface to your program carefully to avoid a situation where you might be forced into manipulating paths. Try to implement the algorithms for your program to similarly avoid the need to manipulate paths. Document the api functions that your program uses on each OS to the user - this is because OS api functions themselves become deprecated over time so in future your program might not be compatible with all possible paths even if you are careful to avoid path manipulation.
2. Document the functions your program uses to manipulate paths
Document to the user exactly how paths will be manipulated. Then make it clear that it is the users responsibility to specify paths that will work correctly with the documented program behavior.
3. Only support a restricted set of paths
Restrict the path mini-languages your program will accept until you are confident that you can correctly manipulate the subset of paths that meet this set of restrictions. Document this to the user. Error if paths are input that do not conform.
4. Ignore the issues
Do some basic path manipulation without worrying too much. Accept that your program will exhibit undefined behavior for some paths that are input. You could document to the user that the program may or may not work when they input paths to it, and that it is the users responsibly to ensure that the program has handled the input paths correctly. However, you could also not document anything. Users will commonly expect that your program will not handle some paths correctly (many don't) and therefore will cope well even without documentation.
Closing thoughts
It is important to decide on an effective strategy for working with paths early on in the life-cycle of your program. If you have to change how paths are handled later it may be difficult to avoid a change in behaviour that might break the your program for existing users.
Try this:
ofstream outfile;
string createFile = "";
string path="/FULL_PATH";
createFile = path.as<string>() + "/" + "SAMPLE_FILENAME" + ".txt";
outfile.open(createFile.c_str());
outfile.close();
//It works like a charm.
That needs to be done when you open the file, see std::ofstream constructor or open() member.
It's not too clear what you're asking; if I understand correctly, you're
given a filename, and you want to create the file in a specific
directory. If that's the case, all that's necessary is to specify the
complet path to the constructor of ofstream. You can use string
concatenation to build up this path, but I'd strongly recommend
boost::filesystem::path. It has all of the functions to do this
portably, and a lot more; otherwise, you'll not be portable (without a
lot of effort), and even simple operations on the filename will require
considerable thought.
I was stuck on this for a while and have since figured it out. The path is based off where your executable is and varies a little. For this example assume you do a ls while in your executable directory and see:
myprogram.out Saves
Where Saves is a folder and myprogram.out is the program you are running.
In your code, if you are converting chars to a c_str() in a manner like this:
string file;
getline(cin, file, '\n');
ifstream thefile;
thefile.open( ("Saves/" + file + ".txt").c_str() );
and the user types in savefile, it would be
"Saves/savefile.txt"
which will work to get to to get to savefile.txt in your Saves folder. Notice there is no pre-slashes and you just start with the folder name.
However if you are using a string literal like
ifstream thefile;
thefile.open("./Saves/savefile.txt");
it would be like this to get to the same folder:
"./Saves/savefile.txt"
Notice you start with a ./ in front of the foldername.
If you are using linux, try execl(), with the command mv.

How to use fstream objects with relative path?

Do I always have to specify absolute path for objects instantiated from std::fstream class? In other words, is there a way to specify just relative path to them such as project path?
You can use relative paths as well. But they are relative to the environment you call your executable from.
This is OS dependent but all the major systems behave more or less the same AFAIK.
Windows example:
// File structure:
c:\folder\myprogram.exe
c:\myfile.txt
// Calling command from folder
c:\folder > myprogram.exe
In the above example you could access myfile.txt with "c:/myfile.txt" or "../myfile.txt". If myprogram.exe was called from the root c:\ only the absolute path would work, but instead "myfile.txt" would work.
As Rob Kennedy said in the comments there's really nothing special about paths regarding fstream. But here is a code example using a relative path:
#include <fstream>
int main() {
std::ifstream ifs("../myfile.txt");
... // Do something sensible with the file
}
If you have an .exe file running from C:\Users\Me
and you want to write a file to C:\Users\Me\You\text.txt,
then all what you need to do is to add the current path operator ., so:
std::ifstream ifs(".\\you\\myfile.txt");
will work
You can use relative paths. They're treated the same as relative paths for any other file operations, like fopen; there's nothing special about fstream in that regard.
Exactly how they're treated is implementation-defined; they'll usually be interpretted relative to your process's current working directory, which is not necessarily the same as the directory your program's executable file lives in. Some operating systems might also provide a single working directory shared by all threads, so you might get unexpected results if a thread changes the working directory at the same time another thread tries to use a relative path.
Say you have a src folder directly under your project directory and the src folder contains another tmp_folder folder which contains a txt file named readMe.txt. So the txt file can be read in this way
std::ifstream fin("../src/tmp_folder/readMe.txt");
The behaviour is OS specific. Therefore, the best way to handle this IMHO is to make it somebody else's problem. Read the path to the file to open as a string from the user (e.g: command line argument, config file, env variable etc..) then pass that string directly to the constructor of fstream. Document that this is how your program behaves.
I wrote more about path manipulation here: https://stackoverflow.com/a/40980510/2345997
You can specify a path relative to current directory. On Windows you may call GetCurrentDirectory to retrieve current directory or call SetCurrentDirectory to set current directory. There are also some CRT functions available.
On linux also:
// main.cpp
int main() {
ifstream myFile("../Folder/readme.txt");
// ...
}
Assuming the folder structure is something like this:
/usr/Douments/dev/MyProject/main.cpp
/usr/Documents/dev/MyProject/Folder/readme.txt
What I ended up using was a relative path as identified on How to open a file with relative path in C++? which ended up being:
myFile.open("../Release/frequency.dat", ios::in);
*changing myFile to whatever your variable is.