the example code on the boost website is not working. http://www.boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/tutorial.html#Using-path-decomposition
int main(int argc, char* argv[])
{
path p (argv[1]); // p reads clearer than argv[1] in the following code
try
{
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 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 != v.end(); ++it)
{
path fn = it->path().filename(); // extract the filename from the path
v.push_back(fn); // push into vector for later sorting
}
}
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;
}
two error messages when compiled in visual studio 2010 for the line path fn = it->path().filename();
The first error is: 'function-style cast' : illegal as right side of '->' operator and the second error is: left of '.filename' must have class/struct/union
Also, when I mouse over path() it says: class boost::filesystem3::path Error: typename not allowed
This section (for's body) is problematic:
path fn = it->path().filename(); // extract the filename from the path
v.push_back(fn); // push into vector for later sorting
it points to path objects, so I don't see why path() is called. Seems that it should be replaced with it->filename()
you push filenames at the end of the same vector, so after the loop you will have the vector containing the list of files twice, first with pathnames then just filenames.
EDIT: Looking at the original example I see that these are your modification. If you want to store filenames instead of printing them, define another vector of string or path and store filenames in it, don't reuse the first one. And removing the call to path() should solve the compilation problem.
EDIT 2: as a cute BTW, you can achieve both directory traversal and filename extraction in one pass using std::transform instead of std::copy:
struct fnameExtractor { // functor
string operator() (path& p) { return p.filename().string(); }
};
vector<string> vs;
vector<path> vp;
transform(directory_iterator(p), directory_iterator(), back_inserter(vs),
fnameExtractor());
Same using mem_fun_ref instead of fnameExtractor functor:
transform(directory_iterator(p), directory_iterator(), back_inserter(vp),
mem_fun_ref(&path::filename));
After you posted the link it seems that the code posted is working fine. But problem is introduced in the modified code in for loop. 1st problem is that, path() is a constructor. 2nd problem, I am not sure about boost::path contains any method as filename(). You can try,
path fn = (*it);
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());
It has been a while that I've written code in C/C++, and I've already found an alternative solution to my problem, but I would like to know why the original code doesn't work.
I have a test class which basically only stores a string.
class test {
private:
std::string name;
public:
test(std::string name) : name(name) {};
std::string get_name() { return name; }
};
In main I have a vector which I at one point fill with test objects. The code below simulates irregular usage of the vector vect.
int main(void) {
std::vector<test *> vect;
std::vector<test *>::iterator i;
//* Comment this for a working example
std::cout << "Searching empty vector" << std::endl;
i = *(_is_in_vector(vect, std::string("test 3")));
if (i == vect.end()) {
std::cout << "Nothing found" << std::endl;
} // */
vect.push_back(new test("test 1"));
vect.push_back(new test("test 2"));
vect.push_back(new test("test 3"));
std::cout << "All:" << std::endl;
i = *(_is_in_vector(vect, std::string("test 3")));
if (i != vect.end()) {
std::cout << "Erase " << (*i)->get_name() << std::endl;
vect.erase(i);
delete *i;
}
i = *(_is_in_vector(vect, std::string("test 3")));
if (i == vect.end()) {
std::cout << "Nothing found" << std::endl;
}
std::cout << "Left:" << std::endl;
for (i = vect.begin(); i!=vect.end(); ++i) {
std::cout << (*i)->get_name() << std::endl;
delete *i;
}
vect.clear();
return 0;
}
Because searching in the vector for a test object happens multiple times, I've created the function _is_in_vector that searches a test object and returns the iterator to it.
static std::vector<test *>::iterator * _is_in_vector(std::vector<test *> &vect, std::string find) {
std::string identity = find;
static std::vector<test *>::iterator i = vect.begin();
std::cout << "Vect size: " << vect.size() << std::endl;
for (i; i != vect.end(); ++i) {
std::string tmp = (*i)->get_name(); /* Segmentation fault after filling vector*/
if (0 == identity.compare(tmp)) break;
}
return &i;
}
The problem is that the code above works when I comment out the Searching empty vector part in main. Once the vector is filled with test objects, I call _is_in_vector a second time. The vector in this function does have three entries, but (*i) all point to NULL.
Output:
Searching empty vector
Vect size: 0
Nothing found
All:
Vect size: 3
Segmentation fault
Expected output:
Searching empty vector
Vect size: 0
Nothing found
All:
Vect size: 3
Erase test 3
Vect size: 2
Nothing found
Left:
test 1
test 2
First of all it is unclear why you need to store test objects by pointer but not by value. If you do need it use smart pointer.
As for your problem, why do you return pointer to an iterator? This is the root cause of your problem - to make &i legal to return you made it static, but static local variables initialized only once and do not change value btw calls - so after first call it pointed to an element in the vector, but after that you added elements and invalidated all iterators including static i hense the segmentation fault. So fix is simple - return iterator by value and make i non static but regular, it is light and it is totally fine to do so.
PS Identifiers starting with _ are illegal in global context, details can be found here What are the rules about using an underscore in a C++ identifier?
So your function actually should look like this:
static std::vector<test *>::iterator is_in_vector( std::vector<test *> &vect, const std::string &find)
{
return std::find_if( vect.begin(), vect.end(), [find]( test *p ) {
return p->get_name() == find;
} );
}
assuming the vector should never hold nullptr, if it is the case or to play safe change condition to:
return p && p->get_name() == find;
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.
How can I search for only folders (while iterating through a directory using boost)? How can I filter my results to get only folders?
I have tried by the names but folders can be strangely named to appear as if they have extensions and I have to deal with that as I am trying to robustly search through a very big and very messy directory (I am not allowed to simply clean it up, it must be searched and filtered as-is...)
Thanks
The question does not include what has already been tried, so if not already done, then the following would definitely help:
http://www.boost.org/doc/libs/1_49_0/libs/filesystem/v3/doc/tutorial.html#Directory-iteration
Directory iteration plus catching exceptions - (tut3.cpp)
Boost.Filesystem's directory_iterator class is just what we need here. It follows the general pattern of the standard library's istream_iterator. Constructed from a path, it iterates over the contents of the directory. A default constructed directory_iterator acts as the end iterator.
The value type of directory_iterator is directory_entry. A directory_entry object contains a path and file_status information. A directory_entry object can be used directly, but can also be passed to path arguments in function calls.
The other need is increased robustness in the face of the many kinds of errors that can affect file system operations. We could do that at the level of each call to a Boost.Filesystem function (see Error reporting), but it is easier to supply an overall try/catch block.
tut3.cpp
int main(int argc, char* argv[])
{
path p (argv[1]); // p reads clearer than argv[1] in the following code
try
{
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 containing:\n";
copy(directory_iterator(p), directory_iterator(), // directory_iterator::value_type
ostream_iterator<directory_entry>(cout, "\n")); // is directory_entry, which is
// converted to a path by the
// path stream inserter
}
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;
}
I've changed some code to convert a std::set to a boost::ptr_set. However, the code doesn't compile; the problem is that I'm assuming that the return value from a ptr_set insert is the same as a set insert (a pair<myIter, bool>). After an hour on Google I found this, and it turns out that the return value from a ptr_set insert appears to be a bool.
Is there any definitive documentation on the differences between the ptr containers and the std containers? I haven't found anything on the boost website, but maybe I'm just being dumb...
EDIT
Ok - what was confusing me was that this code
t.insert(s.release(s.begin()));
p = t.insert(s.release(s.begin()));
reports no error on the first line on gcc, but reports no match for operator= on the second line, so I thought the error was in the return type. However, if you comment out the second line, the first line is then reported as an error (release doesn't return an iterator). My confusion was compounded by the link I posted, in which ptr_container's author states that "insert() in ptr_set<> returns bool". However, reading on down the link it becomes obvious that the code hadn't been finished at the time. Thanks Kerrek.
The following code works as expected, and the interface is the same as for std::set::insert():
#include <boost/ptr_container/ptr_set.hpp>
#include <boost/assign/ptr_list_inserter.hpp>
#include <iostream>
int main()
{
boost::ptr_set<int> s;
{
auto p = s.insert(new int(4));
std::cout << "Element " << *p.first << (p.second ? " inserted" : " already existed") << std::endl;
}
{
auto p = s.insert(new int(4));
std::cout << "Element " << *p.first << (p.second ? " inserted" : " already existed") << std::endl;
}
boost::assign::ptr_insert(s)(1)(2)(3)(4);
for (auto it = s.begin(), end = s.end(); it != end; ++it) { std::cout << *it << "\n"; }
}
The documentation is perhaps not the easiest to navigate, but it's all there. You should look for the "set adapter", though, perhaps that's not entirely obvious.