I'm having some trouble with the following function. It is supposed to be given a path and a set of allowed file extensions, then find all files in that path with any of those extensions. Instead it finds nothing and returns an empty set.
std::set<boostfs::path> scan_directory(const boostfs::path& p,
const bool recurse,
const std::set<std::string>& allowed) {
std::string ext ;
std::set<boostfs::path> incs, incs2 ;
boostfs::path::iterator itr ;
// Extract directory and filename
boostfs::path file = p.filename() ;
boostfs::path dir = p.parent_path() ;
std::cout << "path: " << p.string() << std::endl ;
for (itr = dir.begin(); itr != dir.end(); ++itr) {
if (boostfs::is_directory(*itr)) {
if (recurse) {
std::cout << "dir: " << itr->string() << std::endl ;
incs2 = scan_directory(*itr, true, allowed) ;
incs.insert(incs2.begin(), incs2.end()) ;
}
} else {
// Don't include the original source
if (*itr != p) {
// Include only allowed file types
ext = itr->extension().string() ;
std::cout << "file: " << itr->string() << std::endl ;
std::cout << "ext: " << ext << std::endl ;
if (allowed.find(ext) != allowed.end()) {
incs.insert(*itr) ;
}
}
}
}
return incs ;
}
The prints to cout are just for debugging. I'm testing it with the following directory structure:
./test/cpp/
foo.cpp
foo.h
test.cpp
./test/cpp/bar/
baz.cpp
baz.h
I invoke the function with the path "test/cpp/test.cpp", recurse true and a set containing one string ".cpp". I get the following output from the prints,
path: test/cpp/test.cpp
dir: test
path: test
file: cpp
ext:
Then the function ends and the rest of the program continues, only it's given an empty set of files so not much to work on. Given the test directory it should return a set containing "test/cpp/foo.cpp" and "test/cpp/bar/baz.cpp".
I'm fairly sure it worked not long ago, but I'm not sure when it broke or what I did that made it do so. I'm sure it's some small, annoying technicality.
I found my problem. I was using path::iterator instead of directory_iterator (or recursive_directory_iterator) so I was looping through the components of the path instead of the contents of the directory. I could have sworn it worked earlier, but that might just have been luck maybe.
Here's my working code
std::set<boostfs::path> scan_directory(const boostfs::path& p,
const bool recurse,
const std::set<std::string>& allowed) {
std::string ext ;
std::set<boostfs::path> incs ;
// Extract directory and filename
boostfs::path file = p.filename() ;
boostfs::path dir = p.parent_path() ;
boostfs::recursive_directory_iterator itr(dir), itr_end ;
while(itr != itr_end) {
if (boostfs::is_directory(*itr)) {
itr.no_push(!recurse) ;
} else {
// Don't include the original source
if (*itr != p) {
// Include only allowed file types
ext = itr->path().extension().string() ;
if (allowed.find(ext) != allowed.end()) {
incs.insert(*itr) ;
}
}
}
itr++ ;
}
return incs ;
}
I'll let it be known that the examples of iterating through directories in Boost's documentation are AWFUL
Related
I want to implement recursive directories and files listing on my own. I do not want to use
std::filesystem::recursive_directory_iterator
I tried this code:
void TraverseDirectory(const std::string& rootDirectory)
{
//Go thru the root directory
for(const auto& entry : std::filesystem::directory_iterator(rootDirectory)) {
std::string filenameStr = entry.path().filename().string();
//if the first found entry is directory go thru it
if(entry.is_directory()) {
std::cout << "Dir: " << filenameStr << '\n';
TraverseDirectory(filenameStr);
}
//print file name
else if(entry.is_regular_file()) {
std::cout << "file: " << filenameStr << '\n';
}
}
}
int main()
{
TraverseDirectory("testdir");
}
but it gives me this error when the main loop enters TraverseDirectory(filenameStr);:
How can I iterate over directories and its files without the error shown above?
std::filesystem::path::filename
Returns the generic-format filename
component of the path.
Equivalent to relative_path().empty() ? path() : *--end().
This means, that for actual path /foo/bar/42.txt you get 42.txt return. Now, here in
if(entry.is_directory()) {
std::cout << "Dir: " << filenameStr << '\n';
TraverseDirectory(filenameStr);
}
Your recursive call receives only filename part of path, hence tries to walk into bar, instead of foo/bar for example.
So you better off changing that to
TraverseDirectory(entry.path());
So I can print all the files in a folder but i'd like to print the files that I want. What I mean by that is that I will input for example Mineract, if I have in the folder, for example minecraft_server,mineract launcher. It would print all the names with Minecraft in they're names so it would print Minecraft server and Mineacraft Launcher
I've tried putting it in a for loop. But i can not do the I position of path it's not possible.
for (const auto& entry : fs::directory_iterator(path))
{
cout << entry.path() << endl;
}
That would just print all the files.
UPDATED CODE (still doesn't work).
search - What ever the user inputs
for (const auto& entry : fs::directory_iterator(path)) {
if (entry.path().string().find(search) != string::npos) {
cout << entry.path().string() << endl;
}
}
If I understand your question correctly which I seriously doubt, you want to loop through a folder and its subfolders and only do something for files that contain a certain string.
The following (off top of my head) would work
#include <experimental/filestream>
namespace fs = std::experimental::filestream
for (auto& file : fs::recursive_directory_iterator(yourPath))
{
if(file.path().u8string().find(yourString) != std::string::npos)
do your stuff
}
This example comes straight from code I used for 8 weeks straight and it never failed on me:
for (auto file : fs::recursive_directory_iterator("./"))
{
//std::cout << file.path().u8string() << std::endl;
if (includedFiles.find(file.path().u8string()) != includedFiles.end()
|| skipFile(config, files, &file)
|| file.path().u8string().find((*config)["testFile"].get<std::string>()) != std::string::npos
|| file.path().u8string().find((*config)["outputFile"].get<std::string>()) != std::string::npos
|| matchRegex(&fileOrder, &file.path().u8string())) // Last one does ordering
{
//if (file.path().u8string().find("ValidateModel") != std::string::npos)
//{
// std::cout << "skipped model string " << file.path().u8string() << std::endl;
//}
continue;
}
includedFiles[file.path().u8string()] = true;
std::cout << file.path().u8string() << std::endl;
functor(file);
}
Full code minus the library is available at github: https://github.com/erikknaake/IseProjectSQLFileCombiner/blob/master/SQLFileCombiner.cpp
When you know the name of the folder:
std::string path = std::cin;
for (auto& file : fs::recursive_directory_iterator(path))
{
do your stuff
}
Maybe you need to prepend a /
I am writing a todo list manager program in C++ and would like to do the following:
Check if a directory exists in the program's working directory, if not create it
If it does exist, get a list of .txt files from it.
Be able to create/delete .txt files from this directory
I have tried using boost/filesystem.hpp but can't seem to figure it out (or how to get it to link using g++). Below is an example of what I have tried (assume proper #includes's, int main, etc):
std::vector<std::string> findLists(void){
std::vector<std::string> lists;
std::string temp;
char dir[ MAX_PATH ];
std::string(dir, GetModuleFileName(NULL, dir, MAX_PATH));
dir = dir.substr(0,dir.find_last_of( "\\/" ));
path p(dir);
for(auto i = directory_iterator(p); i != directory_iterator(); i++){
if(!is_directory(i->path())){
temp = i->path().filename().string();
if(temp.compare(0,temp.find(".")+1,".txt")){
temp = temp.substr(0,temp.find("."));
}
lists.push_back(temp);
}
else{
continue;
}
}
return lists;
}
From Boost documentation
int main(int argc, char* argv[])
{
path p (argv[1]); // p reads clearer than argv[1] in the following code
if (exists(p)) // does p actually exist?
{
if (is_regular_file(p)) // is p a regular file?
cout << p << " size is " << file_size(p) << '\n';
else if (is_directory(p)) // is p a directory?
cout << p << "is a directory\n";
else
cout << p << "exists, but is neither a regular file nor a directory\n";
}
else
cout << p << "does not exist\n";
return 0;
}
You have all the facilities you need in there. This is only a starting point, but you should be able to get through it pretty quickly.
I'm using new boost, v1.5.3, to carry out this task like following, thanks to the class recursive_directory_iterator (I don't have to write recursive code):
void ListDirRec(const char *Dir, vector<string>& DirFileList, const char* ext)
{
recursive_directory_iterator rdi(Dir);
recursive_directory_iterator end_rdi;
DirFileList.empty();
string ext_str0(ext);
for (; rdi != end_rdi; rdi++)
{
rdi++;
//cout << (*di).path().string() << endl;
cout << (*rdi).path().string() << endl;
//cout << " <----- " << (*rdi).path().extension() << endl;
//string ext_str1 = (*rdi).path().extension().string();
if (ext_str0.compare((*rdi).path().extension().string()) == 0)
{
DirFileList.push_back((*rdi).path().string());
}
}
the function list files with specific extension. This function works for cases but frequently return an "assertion fails error" like:
**** Internal program error - .... assertion (m_imp.get()) ... operations.hpp(952): dereference of end recursive_directory_iterator
I barely figure out the cause of this error. Can any try.. catch help?
thanks in advance for any help
You are incrementing rdi inside the loop as well as in the for declaration:
for (; rdi != end_rdi; rdi++)
{
rdi++;
That means that rdi might be end_rdi (the end-iterator, which means past the last element) within your loop. Is there any reason you're doing this? (If this is intentional, you should check to make sure rdi != end_rdi again after you increment it.)
You could try something like this:
recursive_directory_iterator dir(path(Dir));
for(auto&& i : dir) {
if (is_directory(i)) {
//Do whatever you want
cout << i << endl;
}
}
I am working on a project to make a database of the files I have on current directory. And one of the details I want about my files is the file permissions that are set with chmod in ubuntu. (just a note: I will be needing the group and owner info too - like chown- and if you could let me know if boost can retrieve the ownership info too that'd be great.)
I am using boost filesystem library and I have checked the documentation for numerous times but couldn't find how to get the permissions.
In this page it shows that there's enum perms that has the file permission strings which doesn't show up on my own filesystem.hpp. (And I have checked that i've got the 1.49 version, also built from the source just to be sure). Also on the same page here it shows that it can get the permissions like:
perms permissions() const noexcept;
//Returns: The value of
//permissions() specified by the postconditions of the most recent call
//to a constructor, operator=, or permissions(perms) function.
I haven't been able to find the permissions function nor the place where it stores the perms list.
This is the code I have so far (which is actually from boost tutorials, but I modified it to be recursive), if you could tell me how to get the file permissions/ownerships or suggest another library than boost I would appreciate it.
EDIT: I have added the s.permissions() as ethan_liou suggested however the output was not as expected. Here's the updated code and the output.
// filesystem tut4.cpp ---------------------------------------------------------------//
// Copyright Beman Dawes 2009
// Distributed under the Boost Software License, Version 1.0.
// See http://www.boost.org/LICENSE_1_0.txt
// Library home page: http://www.boost.org/libs/filesystem
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <boost/filesystem.hpp>
#include <stdio.h>
using namespace std;
using namespace boost::filesystem;
int read(path p);
int main(int argc, char* argv[])
{
if (argc < 2)
{
cout << "Usage: tut4 path\n";
return 1;
}
path p (argv[1]); // p reads clearer than argv[1] in the following code
read(p);
return 0;
}
int read(path p) {
try
{
if (exists(p)) // does p actually exist?
{
if (is_symlink(p)) {
cout << p << " is a link\n";
}
else if (is_regular_file(p)) {
// is p a regular file?
file_status s = status(p);
cout << p << " size is " << file_size(p) << " perms " << "" ;
printf("%o\n",s.permissions());
}
else if (is_directory(p)) // is p a directory?
{
cout << p << " is a directory containing:\n";
typedef vector<path> vec; // store paths,
vec v; // so we can sort them later
copy(directory_iterator(p), directory_iterator(), back_inserter(v));
sort(v.begin(), v.end()); // sort, since directory iteration
// is not ordered on some file systems
for (vec::const_iterator it(v.begin()), it_end(v.end()); it != it_end; ++it)
{
//cout << " " << *it << '\n';
read(*it);
}
}
else
cout << p << " exists, but is neither a regular file nor a directory\n";
}
else
cout << p << " does not exist\n";
}
catch (const filesystem_error& ex)
{
cout << ex.what() << '\n';
}
return 0;
}
Output:
$ ./a.out ~/Desktop/test
"/home/usr/Desktop/test" is a directory containing:
"/home/usr/Desktop/test/a.out" size is 69446 perms 27746424350
"/home/usr/Desktop/test/la" is a directory containing:
"/home/usr/Desktop/test/la/Untitled Document" size is 0 perms 27746424170
"/home/usr/Desktop/test/la/lala" is a directory containing:
"/home/usr/Desktop/test/la/lala/Link to lalalala" is a link
"/home/usr/Desktop/test/la/lala/Untitled Folder" is a directory containing:
"/home/usr/Desktop/test/la/lala/lalalala" size is 0 perms 0
"/home/usr/Desktop/test/test.cpp" size is 2234 perms 0
"/home/usr/Desktop/test/test.cpp~" size is 2234 perms 0
Note: Those numbers that are like 27746424350 change each time the program is executed.
perms permissions() const { return m_perms; }
defined in boost/filesystem/v3/operations.hpp
Add an easy sample code
#include <boost/filesystem.hpp>
#include <stdio.h>
namespace fs=boost::filesystem;
int main(int argc,char * argv[]){
fs::path p(argv[1]);
fs::file_status s = status(p);
printf("%o\n",s.permissions());
}
File permissions example for windows:
unsigned long attributes = ::GetFileAttributes( filePath.file_string().c_str());
if ( attributes != 0xFFFFFFFF && ( attributes & FILE_ATTRIBUTE_READONLY ))
{
attributes &= ~FILE_ATTRIBUTE_READONLY;
::SetFileAttributes( filePath.file_string().c_str(), attributes );
}