How to get the file format in C++/Qt - c++

I need to write a program that allows me to read the names of every file in a directory, and also the type of a file. While I can get the filenames, I am unable to get how I am supposed to find the type. The only thing I can think of is to use its metadata, but I'm not even sure how to begin with that.
I'm trying for an OS independent solution, but I'll be happy if it works only on Windows or Linux only as well.
Note: Please do NOT give solutions that involve searching for a dot, and anything in between. These days, almost no file had a .pdf or something attached to it. When you read the filename, all you get is sample, and not sample.txt.
Also, if it is not compatible with Qt, I'd appreciate if you could mention this in your answer so I can look take appropriate actions (I'm working on Qt because of GUI).

File Extension
As #CMLDMR pointed out, QFileInfo::suffix() and QFileInfo::completeSuffix() methods will return the file extension. However, file extension may be omitted, invalid, unknown, etc.
So, according to your actual question, you want to know the file format, not its extension as stated in the first revision.
File Format
Qt 5 has a class QMimeDatabase. It allows you to discover the file MIME type by its contents. Let's say we have an mpeg music file with no extension:
QMimeDatabase db;
QMimeType mime = db.mimeTypeForFile("C:/music", QMimeDatabase::MatchContent);
qDebug() << mime.name(); // Name of the MIME type ("audio/mpeg").
qDebug() << mime.suffixes(); // Known suffixes for this MIME type ("mp3", "mpga").
qDebug() << mime.preferredSuffix(); // Preferred suffix for this MIME type ("mp3").
File Extension + File Format
If you still want to give priority to the file extension and parse the file contents only if the suffix is not present, omit the QMimeDatabase::MatchContent argument – the default QMimeDatabase::MatchDefault will be used. See QMimeDatabase::MatchMode for more informtaion.
Note that Qt uses different MIME databases for different operating systems, so the results may vary.

QFileInfo fi("/tmp/archive.tar.gz");
QString ext = fi.suffix(); // ext = "gz"
suffix function is give you a file extension.
OR
QFileInfo fi("/tmp/archive.tar.gz");
QString ext = fi.completeSuffix(); // ext = "tar.gz"
you can use each of other which one is correct for you.
This code independent but Qt.
For Details LINK

Related

How to read a file name containing 'œ' as character in C/C++ on windows

This post is not a duplicate of this one: dirent not working with unicode
Because here I'm using it on a different OS and I also don't want to do the same thing. The other thread is trying to simply count the files, and I want to access the file name which is more complex.
I'm trying to retrieve data information through files names on a windows 10 OS.
For this purpose I use dirent.h(external c library, but still very usefull also in c++).
DIR* directory = opendir(path);
struct dirent* direntStruct;
if (directory != NULL)
{
while (direntStruct = readdir(directory))
{
cout << direntStruct->d_name << endl;
}
}
This code is able to retrieve all files names located in a specific folder (one by one). And it works pretty well!
But when it encounter a file containing the character 'œ' then things are going crazy:
Example:
grosse blessure au cœur.txt
is read in my program as:
GUODU0~6.TXT
I'm not able to find the original data in the string name because as you can see my string variable has nothing to do with the current file name!
I can rename the file and it works, but I don't want to do this, I just need to read the data from that file name and it seems impossible. How can I do this?
On Windows you can use FindFirstFile() or FindFirstFileEx() followed by FindNextFile() to read the contents of a directory with Unicode in the returned file names.
Short File Name
The name you receive is the 8.3 short file name NTFS generates for non-ascii file names, so they can be accessed by programs that don't support unicode.
clinging to dirent
If dirent doesn't support UTF-16, your best bet may be to change your library.
However, depending on the implementation of the library you may have luck with:
adding / changing the manifest of your application to support UTF-8 in char-based Windows API's. This requires a very recent version of Windows 10.
see MSDN:
Use the UTF-8 code page under Windows - Apps - UWP - Design and UI - Usability - Globalization and localization.
setting the C++ Runtime's code page to UTF-8 using setlocale
I do not recommend this, and I don't know if this will work.
life is change
Use std::filesystem to enumerate directory content.
A simple example can be found here (see the "Update 2017").
Windows only
You can use FindFirstFileW and FindNextFileW as platform API's that support UTF16 strings. However, with std::filesystem there's little reason to do so (at least for your use case).
If you're in C, use the OS functions directly, specifically FindFirstFileW and FindNextFileW. Note the W at the end, you want to use the wide versions of these functions to get back the full non-ASCII name.
In C++ you have more options, specifically with Boost. You have classes like recursive_directory_iterator which allow cross-platform file searching, and they provide UTF-8/UTF-16 file names.
Edit: Just to be absolutely clear, the file name you get back from your original code is correct. Due to backwards compatibility in Windows filesystems (FAT32 and NTFS), every file has two names: the "full", Unicode aware name, and the "old" 8.3 name from DOS days.
You can absolutely use the 8.3 name if you want, just don't show it to your users or they'll be (correctly) confused. Or just use the proper, modern API to get the real name.

C++ char* relative file path? (Qt)

I am trying to play a .mod audio file in an executable. I am using the 3rd party BASSMOD .dll. I can get the audio file to play when I provide the full path to the file, but I cannot get it to play when providing a relative path. Here are some snippets.
main.cpp
#include <QCoreApplication>
#include "bassmod.h"
// define file location
const char* file = "C:/Users/Downloads/test4/console/music.mod";
void startMusic() {
BASSMOD_Init(-1, 44100, 0);
BASSMOD_MusicLoad(FALSE,(void*)file,0,0,BASS_MUSIC_RAMPS);
BASSMOD_MusicPlayEx(0,-1,TRUE);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
startMusic();
return a.exec();
}
bassmod.h (relevant snippet)
BOOL BASSDEF(BASSMOD_MusicLoad)(BOOL mem, void* file, DWORD offset, DWORD length, DWORD flags);
The function I'm concerned about is BASSMOD_MusicLoad. As this project stands, the .mod file will play no problem. However, when I try to change the absolute path of the .mod file to a relative path ("music.mod"), the file fails to play. Why is that? I have the .mod file in the same directory as the executable as well as in the directory containing the .pro file -- that didn't seem to be the issue.
Also, maybe I'm missing something related to how files are opened in C++. It looks like the MusicLoad function requires that the second parameter be of type void*. I'm sure there are many different things I could be doing better here. Ideally, I'd like to be able to have file store the relative path to the .mod file and have it play that way so I don't have to hard code an absolute path. In a perfect world, I would like to supply file with a path to the .mod file in my resources.qrc, but then I would have to use QFile, I believe, which won't work because I need the type to be void*.
Any help for a beginner would be much appreciated.
EDIT 01: Thank you all for your help! I got it to work (using relative file path, at least). There are two ways to do this. Here's what I did and how I tested it:
The first case makes the assumption that BASSMOD (or whatever external dll you're using) does not handle relative paths.
const char* file = "C:/debug/music.mod"; // same dir as .exe
QFileInfo info("music.mod");
QString path = info.absoluteFilePath();
const string& tmp = path.toStdString();
const char* raw = tmp.data();
Those are the test items I set up. When I run BASSMOD_MusicLoad(FALSE,(void*)file,0,0,BASS_MUSIC_RAMPS);, it works as expected. That's when I hard-code the full absolute path.
When I ran BASSMOD_MusicLoad(FALSE,(void*)raw,0,0,BASS_MUSIC_RAMPS);, it didn't work. So I decided to print out the values for everything to see where it's messing up:
cout << "Qstring path: ";
qDebug() << path;
cout << "string& tmp: ";
cout << tmp << endl;
cout << "raw: ";
cout << raw << endl;
cout << "full char* file: ";
cout << file;
startMusic();
...returns this:
Qstring path:
"C:/myApp/build-Debug/music.mod"
string& tmp:
C:/myApp/build-Debug/music.mod
raw:
C:/myApp/build-Debug/music.mod
full char* file:
C:/myApp/build-Debug/debug/music.mod
Note the difference? When I hard-code the full path to the file, I found that (thanks to #FrankOsterfeld and #JasonC) the current working directory was actually not where the .exe (/debug) or .pro files were located. It was actually in the same directory as the Makefile.
So I just changed it to this: QFileInfo info("./debug/x.m"); and it worked.
Even though the problem wound up being me not knowing where the current working directory was, the solutions by #Radek, #SaZ, and #JasonC helped to find another way to solve this (plus it showed me how to get the working dirs and convert between types). This is a good reference for people who would want to use QFileInfo to determine where you actually are in the filesystem. I would have used this solution if the dll I was using did not handle relative paths well. However...
I wondered if I could apply the same solution to my original code (without using QFileInfo and converting types, etc). I assumed that BASSMOD did not handle relative paths out of the box. I was wrong. I changed the file variable to const char* file = "./debug/x.m"; It worked!
Thanks for the help, everyone!
However, I would still like to get this to work using music.mod from a Qt resources file. Based on the replies, though, it doesn't look like that's possible unless the 3rd party library you're using supports the Qt resource system.
I have the .mod file in the same directory as the executable.
In Qt Creator the default initial working directory is the directory that the .pro file is in, not the directory that the .exe ends up in.
Either put your file in that directory (the one that probably has all the source files and such in it as well, if you used the typical setup), or change the startup directory to the directory the .exe file is in (in the Run Settings area).
Although, based on your new comment below, I guess the problem is deeper than that... I can't really tell you why BASS doesn't like relative filenames but you can convert a relative path to an absolute one before passing it to BASS. There's a lot of ways to do that; using Qt's API you could:
#include <QFileInfo>
...
const char* file = "music.mod"; // Your relative path.
...
BASSMOD_MusicLoad(...,
(void*)QFileInfo(file).absoluteFilePath().toAscii().data(),
...);
In a perfect world, I would like to supply file with a path to the .mod file in my resources.qrc
You won't be able to do that because loading resources from .qrc files is a Qt thing and BASS presumably does not use Qt internally (just like e.g. you could not open a resource with fopen), and doesn't understand how to load resources embedded by Qt. I am not familiar with BASS but a cursory glance at this documentation shows that it also has the ability to play data from an in-memory buffer. So one approach would be to use Qt to load the resource into accessible memory and pass that buffer instead.
In a perfect world, I would like to supply file with a path to the .mod file in my resources.qrc, but then I would have to use QFile, I believe, which won't work because I need the type to be void*.
Why do you only belive? Read Qt Doc. It will work. Don't use class QFile but QFileInfo.
QFileInfo info(:/resourcePrefix/name);
QString path = info.absoluteFilePath();
void* rawPtr = (void*)path.toStdString().c_str();

Recovering Files on Windows and C

Well this time I'm trying to write a program in C which recover deleted files from a disk, it could be an external disk, I have an idea than i had used before on linux, it is to open the disk as a kind of file and scaning the Headers and file footers of everything within the disk, the point is I'm not sure if there's allow on windows to open a disk as an File, basiclly I have the logic how to develope this program, but I'm not sure how to implement it on windows, anybody can give me a hand with this?.
The code I used on linux to open a disk as a file was:
Edit: That was a sample of what I was using guys, it's just to give you an idea of what I was doing, the correct syntax I used was the next:
direccion = ui->linea->text().toLatin1().constData();
f = fopen(direccion,"rb");
I used QT creator on linux, and direccion variable was a TextField value which contained the file path of the disk through a button function that open a QFileDialog...
could I use it in windows as well?
Thank you before hand..
"The code I used on linux to open a disk as a file was:"
File *f = fopen("E:\", "rb");
I seriously doubt you ever got this code working on any linux system (or windows either).
You'll need to escape the backslash path delimiter, if it's presented in any string literal:
FILE* f = fopen("E:\\", "rb");
// ^
Also all that filesystem path style you are presenting to access a particular disk, is about accessing a windows file path/disk.
No linux file system has notion about drive characters, and the file path delimiter value used is '/', not '\\'.
To recover deleted files, you can't use fopen or fstream::open because the file was deleted. Check the return value from the function or test the stream state.
The way to recover deleted files is:
Get the Master File Table as raw data.
Search for the record containing a string similar to the deleted
filename.
Change the entry in the Master File Table to "undeleted".
Write the Master File Table back to the drive.
The above usually requires platform specific API, which is different on Linux and Windows platforms.

QSettings: Is it possible to disable groups?

I am using QSettings to write an .ini file which will act as my applications configuration file. There is only one problem:
QSettings adds a group name [General] to the top of the file.
I set up the file this way:
QSettings settings(QApplication::applicationDirPath() + fileName, QSettings::IniFormat);
and write to it like so:
settings.setValue("some_setting", theNumber);
However, the program I am ultimately feeding this data to cannot deal with the [General] tag. While it is certainly an option for me to manually delete [General] from the file, I wonder if it is possible to make QSettings stop doing this.
I suspect the behavior is due to specifying QSettings::IniFormat. However, I do not see any other options that would tell it to stop specifying groups.
Any idea how I can do this?
The .ini file format is a de-facto standard based on Microsoft's implementation from Windows. It requires the presence of sections.
Whatever application you feed the file to, doesn't really implement an .ini file format, but something else.
Most likely you shouldn't be using QSettings at all, but implement the functionality manually. This shouldn't be a problem, since you only do it to "feed" the data to some other piece of software. You don't need to read it back. You can store your settings in regular QSettings, and then export them to a text file for that application's perusal.
It's time to write your own QSettings file format.
"file format" is a pair of two metods with signatures:
bool readXmlFile(QIODevice &device, QSettings::SettingsMap &map);
bool writeXmlFile(QIODevice &device, const QSettings::SettingsMap &map);
one should fill map from device, second should dump map to device. You can use there anything you want -- xml, plain text files without [general] group etc.
After it, you should register your new shiny format with QSettings::registerFormat function.
Here is documentation: http://qt-project.org/doc/qt-5/qsettings.html#registerFormat
good luck.

Ensure File Extension Matches File Type in C++

all. I am trying to write a C++ program that will iterate through a user-specified directory (e.g. /home/alpernick/Pictures). Primarily, this is to ensure that there are no duplicates (checked via md5sum).
But one feature I truly want to include is to ensure that the extension of a filename matches the file's type.
For example, if the file's name is "sunrise.png" I want to ensure that it actually is indeed a PNG and not a mislabeled JPEG (for example).
I am approaching this with four functions, as follows.
string extension(string fileName) // returns the extension of fileName (including .tar.gz handling, so it isn't blindly just returning the last 3 characters)
string fileType(string fileName) // This one is the key -- it returns the actual file type, so if the file named fileName is a PNG, fileType() will return PNG, regardless of the return value of extension()
string basename(string fileName) // Rerturns the basename of the file, I.e. everything before the extension (so, for sunset.jpg, it would return sunset; for fluffytarball,tar.gz, it would return fluffytarball)
string renameFile(string incorrectFileName, string fileNameBeforeExtension, string actualFileType) // Returns a string whose value is the basename concatenated with the correct file extension.
string file = sunset.jpg;
/* Setting file to be hard-coded for illustrative purposes only */
if(extension(file) != fileType(file)
{
char fixedName [] = renameFile(file, basename(file), fileType(file));
puts(fixedName);
}
I have zero issues with the string processing. I'm stuck, however, on fileType(). I want this program to not only run on my primary machine (Kubuntu 14.04), but also to be capable of being run on a Windows machine as well. So, it seems I need some library or set of libraries that would be common to both (or at the least compiled for both).
Any help/advice?
There are more exceptions than rules for guessing the actual type of a file based on its contents.
This is exacerbated by the fact that a file can be valid and useful interpreted as two completely different file types.
For a good program trying to guess on insufficient data, try file on Unixoids.
You could try looking at file source code: https://github.com/file/file .
But as wikipedia states
file's position-sensitive tests are normally implemented by matching various locations within the file against a textual database of magic numbers (see the Usage section). This differs from other simpler methods such as file extensions and schemes like MIME.
In most implementations, the file command uses a database to drive the probing of the lead bytes. That database is implemented in a file called magic, whose location is usually in /etc/magic, /usr/share/file/magic or a similar location.
So it does not seem trivial.