C:\Projects\Logs\RTC\MNH\Debug
C:\Projects\Logs\FF
Is there an expression/string that would say go back until you find "Logs" and open it? (assuming you were always below it)
The same executable is run out of "Debug", "MNH" or "FF" at different times, the executable always should save it's log files into "Logs".
What expression would get there WITHOUT referring to the entire path C:\Projects\Logs?
Thanks.
You might have luck using the boost::filesystem library.
Without a compiler (and ninja-copies from boost documentation), something like:
#include <boost/filesystem.hpp>
namespace boost::filesystem = fs;
bool contains_folder(const fs::path& path, const std::string& folder)
{
// replace with recursive iterator to check within
// sub-folders. in your case you just want to continue
// down parents paths, though
typedef fs::directory_iterator dir_iter;
dir_iter end_iter; // default construction yields past-the-end
for (dir_iter iter(path); iter != end_iter; ++iter)
{
if (fs::is_directory(iter->status()))
{
if (iter->path().filename() == folder)
{
return true;
}
}
}
return false;
}
fs::path find_folder(const fs::path& path, const std::string& folder)
{
if (contains_folder(path, folder))
{
return path.string() + folder;
}
fs::path searchPath = path.parent_path();
while (!searchPath.empty())
{
if (contains_folder(searchPath, folder))
{
return searchPath.string() + folder;
}
searchPath = searchPath.parent_path();
}
return "":
}
int main(void)
{
fs::path logPath = find_folder(fs::initial_path(), "Log");
if (logPath.empty())
{
// not found
}
}
For now this is completely untested :)
It sounds like you're asking about a relative path.
If the working directory is C:\Projects\Logs\RTC\MNH\Debug\, the path ..\..\..\file represents a file in the Logs directory.
If you might be in either C:\Projects\Logs\RTC\MNH\ or C:\Projects\Logs\RTC\MNH\Debug\, then no single expression will get you back to Logs from either place. You could try checking for the existence of ..\..\..\..\Logs and if that doesn't exist, try ..\..\..\Logs, ..\..\Logs and ..\Logs, which one exists would tell you how "deep" you are and how many ..s are required to get you back to Logs.
Related
Currently I am using dirent.h to iterativly access files in a directory and its sub-directories. It accesses them according to their file names (alphabetically). For some reason I want to access the subdirectories and the files according to their modification time (with the latest modified file being accessed at last). Is it possible to do so?
I am thinking to first traverse through the directories and create a sorted list of the files according to their modification time and use this list to read them accordingly. But I was hoping if there is a better way.
Based on my first thinking and as per the suggestion in the comments.
#include<dirent.h>
typedef struct files {
long long mtime;
string file_path;
bool operator<(const files &rhs)const {
return mtime < rhs.mtime;
}
}files;
using namespace std;
vector<struct files> files_list;
void build_files_list(const char *path) {
struct dirent *entry;
DIR *dp;
if (dp = opendir(path)) {
struct _stat64 buf;
while ((entry = readdir(dp))){
std::string p(path);
p += "\\";
p += entry->d_name;
if (!_stat64(p.c_str(), &buf)){
if (S_ISREG(buf.st_mode)){
files new_file;
new_file.mtime = buf.st_mtime;
new_file.file_path = entry->d_name;
files.push_back(new_file);
}
if (S_ISDIR(buf.st_mode) &&
// the following is to ensure we do not dive into directories "." and ".."
strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")){
//do nothing
}
}
}
closedir(dp);
}
else{
printf_s("\n\tERROR in openning dir %s\n\n\tPlease enter a correct
directory", path);
}
}
void main(){
char *dir_path="dir_path";
build_files_list(dir_path);
sort(files_list.begin(), files_list.end());
//the rest of things to do
}
I am trying to remove all directories, subdirectories and the contained files from a specific path using boost::filesystem::remove_all(path). I also want to display an error message in case a file is open in another program. Does boost::filesystem::remove_all(path) throw an exception in this case?
Or is there another way I can achieve this?
this does not fit in a comment so I'm posting as an answer
Just look in the source: http://www.boost.org/doc/libs/1_55_0/libs/filesystem/src/operations.cpp
BOOST_FILESYSTEM_DECL
boost::uintmax_t remove_all(const path& p, error_code* ec)
{
error_code tmp_ec;
file_type type = query_file_type(p, &tmp_ec);
if (error(type == status_error, tmp_ec, p, ec,
"boost::filesystem::remove_all"))
return 0;
return (type != status_error && type != file_not_found) // exists
? remove_all_aux(p, type, ec)
: 0;
}
remove_all_aux is defined few lines above and so is remove_file_or_directory, remove_file, remove_directory and so forth and so on. The primitive operations are:
# if defined(BOOST_POSIX_API)
...
# define BOOST_REMOVE_DIRECTORY(P)(::rmdir(P)== 0)
# define BOOST_DELETE_FILE(P)(::unlink(P)== 0)
...
# else // BOOST_WINDOWS_API
...
# define BOOST_REMOVE_DIRECTORY(P)(::RemoveDirectoryW(P)!= 0)
# define BOOST_DELETE_FILE(P)(::DeleteFileW(P)!= 0)
...
# endif
The behavior of removing a locked file will be whatever your platform will provide.
I am posting some code examples to clarify this issue.
There are 2 scenarios.
In the first scenario I am using the remove_all function to delete the whole directory from a certain path and then I create a new directory at the same path:
try
{
if(exists(directory_path))
{
remove_all(directory_path);
}
create_directory(directory_path);
}
catch(filesystem_error const & e)
{
//display error message
}
This works just as expected, but then I have a second scenario where I am trying to delete certain folders from a path and then create the new directory:
try
{
if(exists(directory_path))
{
for ( boost::filesystem::directory_iterator itr(directory_path); itr != end_itr; itr++)
{
std::string folder = itr->path().filename().string();
if(folder == FOLDER1 || folder == FOLDER2 || folder == FOLDER3)
remove_all(itr->path());
}
}
create_directory(directory_path);
}
catch(filesystem_error const & e)
{
//display error message
}
In this case the exception is not thrown in case a file is open in another program. The files just get deleted. Why does this happen?
It depends on which overload of remove_all you call; this is clearly documented in the documentation for the function. (What isn't clear is, if you use the function which reports errors by means of an error code, does the function continue, or does it return after the first error?)
In my project, I need to show all files on user's drive filtered by the filename with a text line. Are there any APIs to do such thing?
On Windows, I know, there're FindFirstFile and FindNextFile functions in WinAPI.
I use C++/Qt.
There's ftw() and linux has fts()
Besides those, you can iterate directories, using e.g. opendir8/readdir()
Qt provides the QDirIterator class:
QDirIterator iter("/", QDirIterator::Subdirectories);
while (iter.hasNext()) {
QString current = iter.next();
// Do something with 'current'...
}
If you are looking for a Unix command, you could do this :
find source_dir -name 'regex'
If you want to do it C++ style, I'd suggest to use boost::filesystem. It's a very powerfull cross platform library.
Of course, you will have to add an additional library.
Here is an example :
std::vector<std::string> list_files(const std::string& root, const bool& recursive, const std::string& filter, const bool& regularFilesOnly)
{
namespace fs = boost::filesystem;
fs::path rootPath(root);
// Throw exception if path doesn't exist or isn't a directory.
if (!fs::exists(rootPath)) {
throw std::exception("rootPath does not exist");
}
if (!fs::is_directory(rootPath)) {
throw std::exception("rootPath is not a directory.");
}
// List all the files in the directory
const std::regex regexFilter(filter);
auto fileList = std::vector<std::string>();
fs::directory_iterator end_itr;
for( fs::directory_iterator it(rootPath); it != end_itr; ++it) {
std::string filepath(it->path().string());
// For a directory
if (fs::is_directory(it->status())) {
if (recursive && it->path().string() != "..") {
// List the files in the directory
auto currentDirFiles = list_files(filepath, recursive, filter, regularFilesOnly);
// Add to the end of the current vector
fileList.insert(fileList.end(), currentDirFiles.begin(), currentDirFiles.end());
}
} else if (fs::is_regular_file(it->status())) { // For a regular file
if (filter != "" && !regex_match(filepath, regexFilter)) {
continue;
}
} else {
// something else
}
if (regularFilesOnly && !fs::is_regular_file(it->status())) {
continue;
}
// Add the file or directory to the list
fileList.push_back(filepath);
}
return fileList;
}
you can also use glob
http://man7.org/linux/man-pages/man3/glob.3.html
has the advantage of existing on a lot of Unices (Solaris for sure) as it is part of POSIX.
Ok, it's not C++ but pure C.
Look man find. find supports filtering by a mask ( -name option for examole)
I'm trying to find the type of file a path is. I have this for Linux::
pathType CFilesystem::findPathType(const string& path) const
{
struct stat info;
int status = stat(path.c_str(), &info);
if(status == -1)
{
switch(errno)
{
case ENOENT: // A component of the path does not exist.
return pathType::none;
default:
return pathType::unknown;
}
}
if(S_ISDIR(info.st_mode))
{
return pathType::directory;
}
if(S_ISREG(info.st_mode))
{
return pathType::file;
}
return pathType::unknown;
}
But I'm not sure how to do the same for Windows. _stat doesn't seem to work (it says a file doesn't exist, even know I'm POSITIVE it exists. After all, the programming is running from it.
In windows, I think the function you are looking for is GetFileAttributesEx
You can also use the regular _stat function on windows as well though. Are you including sys/types.h and sys/stat.h?
_stat should work just fine. I would try to find out what path your function is trying to resolve, printf-debugging or debugger-wise. It must be something obvious. (The result of the printfdebugging can be appended to your question, next to a directory tree proving that the file is there :) )
This has already been solved for you:
pathType CFilesystem::findPathType(string const &path) const {
using namespace boost::filesystem;
boost::filesystem::path p (path);
switch (status(p)) {
case directory_file: return pathType::directory;
case file_not_found: return pathType::none;
case regular_file: return pathType::file;
default: return pathType::unknown;
}
}
What is the cleanest way to recursively search for files using C++ and MFC?
EDIT: Do any of these solutions offer the ability to use multiple filters through one pass? I guess with CFileFind I could filter on *.* and then write custom code to further filter into different file types. Does anything offer built-in multiple filters (ie. *.exe,*.dll)?
EDIT2: Just realized an obvious assumption that I was making that makes my previous EDIT invalid. If I am trying to do a recursive search with CFileFind, I have to use *.* as my wildcard because otherwise subdirectories won't be matched and no recursion will take place. So filtering on different file-extentions will have to be handled separately regardless.
Using CFileFind.
Take a look at this example from MSDN:
void Recurse(LPCTSTR pstr)
{
CFileFind finder;
// build a string with wildcards
CString strWildcard(pstr);
strWildcard += _T("\\*.*");
// start working for files
BOOL bWorking = finder.FindFile(strWildcard);
while (bWorking)
{
bWorking = finder.FindNextFile();
// skip . and .. files; otherwise, we'd
// recur infinitely!
if (finder.IsDots())
continue;
// if it's a directory, recursively search it
if (finder.IsDirectory())
{
CString str = finder.GetFilePath();
cout << (LPCTSTR) str << endl;
Recurse(str);
}
}
finder.Close();
}
Use Boost's Filesystem implementation!
The recursive example is even on the filesystem homepage:
bool find_file( const path & dir_path, // in this directory,
const std::string & file_name, // search for this name,
path & path_found ) // placing path here if found
{
if ( !exists( dir_path ) ) return false;
directory_iterator end_itr; // default construction yields past-the-end
for ( directory_iterator itr( dir_path );
itr != end_itr;
++itr )
{
if ( is_directory(itr->status()) )
{
if ( find_file( itr->path(), file_name, path_found ) ) return true;
}
else if ( itr->leaf() == file_name ) // see below
{
path_found = itr->path();
return true;
}
}
return false;
}
I know it is not your question, but it is also easy to to without recursion by using a CStringArray
void FindFiles(CString srcFolder)
{
CStringArray dirs;
dirs.Add(srcFolder + "\\*.*");
while(dirs.GetSize() > 0) {
CString dir = dirs.GetAt(0);
dirs.RemoveAt(0);
CFileFind ff;
BOOL good = ff.FindFile(dir);
while(good) {
good = ff.FindNextFile();
if(!ff.IsDots()) {
if(!ff.IsDirectory()) {
//process file
} else {
//new directory (and not . or ..)
dirs.InsertAt(0,nd + "\\*.*");
}
}
}
ff.Close();
}
}
Check out the recls library - stands for recursive ls - which is a recursive search library that works on UNIX and Windows. It's a C library with adaptations to different language, including C++. From memory, you can use it something like the following:
using recls::search_sequence;
CString dir = "C:\\mydir";
CString patterns = "*.doc;abc*.xls";
CStringArray paths;
search_sequence files(dir, patterns, recls::RECURSIVE);
for(search_sequence::const_iterator b = files.begin(); b != files.end(); b++) {
paths.Add((*b).c_str());
}
It'll find all .doc files, and all .xls files beginning with abc in C:\mydir or any of its subdirectories.
I haven't compiled this, but it should be pretty close to the mark.
CString strNextFileName , strSaveLog= "C:\\mydir";
Find.FindFile(strSaveLog);
BOOL l = Find.FindNextFile();
if(!l)
MessageBox("");
strNextFileName = Find.GetFileName();
Its not working. Find.FindNextFile() returning false even the files are present in the same directory``