My earlier posts on C++ Boost Libraries introduced the Boost Filesystem Library and discussed some example programs that make use of it. I discuss two more examples in this post. (Be sure to include the needed boost header files as noted in the earlier posts, including creation of the alias to the boost filesystem namespace: namespace bfs = boost::filesystem; )
1. A function to search for a file.
C++:
// Search for a file with the name 'filename' starting in directory 'dir_path', copy the path of the file in 'pfound' if found, and return true.
// Else return false.
bool find_file(const bfs::path & dir_path, const std::string & file_name, bfs::path & pfound)
{
if( !exists(dir_path) || !is_directory(dir_path) )
return false;
bfs::directory_iterator iter(dir_path), end_iter;
for(; iter!= end_iter; ++iter)
{
if( bfs::is_directory(*iter) )
{
if( find_file(*iter, file_name, pfound) )
return true;
}
else if( iter->leaf() == file_name )
{
pfound = *iter;
return true;
}
}
return false;
}
The function first verifies if the path passed in 'path_dir' exists and if it is a directory. Then it creates a 'directory_iterator' object(introduced in the previous post) by passing this path to its constructor. The loop then iterates over all the contents of the directory, and for every sub-directory found, it calls itself recursively passing this new directory as the starting point. Every file that is found is compared with the file that is to be searched(file_name), and if a match is found, the path is copied into 'pfound' and function returns true. If nothing is found by the end of the loop, 'false' is returned. Remember that as discussed earlier, iter->leaf() returns the last part of a path - the file name. Applying indirection operator to the directory_iterator object returns the file it is pointing to.
2. A simple directory listing (ls/dir) program.
C++:
void sls(const bfs::path & p)
{
unsigned long fc=0, dc=0;
if( !bfs::exists(p) )
std::cout<<"\nFile Not Found:"<<p.native_file_string()<<"\n";
else if( !bfs::is_directory(p) )
std::cout<<"\nFound: " <<p.native_file_string() <<"\n";
std::cout<<"In directory:"<<p.native_file_string()<<"\n";
bfs::directory_iterator iter(p), end_iter;
for(; iter != end_iter; ++iter)
{
try {
if(bfs::is_directory(*iter))
{
++dc;
std::cout<<iter->leaf()<<"[Directory]\n";
}
else
{
++fc;
std::cout<<iter->leaf()<<"\n";
}
} catch(const std::exception & ex) {
std::cout<<iter->leaf() <<": " <<ex.what() <<std::endl;
}
std::cout<<fc<<" "<<dc<<std::endl;
} //for
} //sls
The function simply iterates over all the contents of the directory passed to it(p) and prints every file and directory that is found. [Directory] tag is added to the directory names, and at the end of the program the total count of the files and directories is printed. This program only prints the contents of the directory passed, it doesn't traverse its sub-directories. Can you combine the general idea of the above two examples to create a directory listing program that displays the files of all the sub-directories recursively? You can also print more information about the files by using boost filesystem functions like is_symbolic_link(), file_size(), last_write_time() etc[discussed in the earlier posts].
Most of the examples inspired/taken from the Boost website. The website documentation also contains more help on the various formats in which the paths to the files can be represented. The function 'native_file_string()' used in the Example 2 above returns the path in the native format(using '/' as a file separator on Unix platforms and '\' on Windows platforms, for example). To pass the path names as 'path' objects, use the services from fstream.hpp. All Filesystem library related exceptions are available in exception.hpp. The header convenience.hpp contains a few convenience functions(like change_extension()). You can start from the documentation page on the website for further information.
Related Posts:
Overall I have found your post helpful regarding the use of the boost:filesystem classes. Just a comment/question on style: Why most everyone that has presented the directory iterators capability always uses a function with four return statements (I found it c-ish, confusing, and annoying). You can easily modify the code so as to leave only one exit point. For instance:
bool find_file(const bfs::path & dir_path, const std::string & file_name, bfs::path & pfound)
{
bool successFlag(false);
if( exists(dir_path) && is_directory(dir_path) )
{
bfs::directory_iterator iter(dir_path), end_iter;
for(; iter!= end_iter; ++iter)
{
if( bfs::is_directory(*iter) )
{
if( find_file(*iter, file_name, pfound) )
successFlag = true;
break;
}
else if( iter->leaf() == file_name )
{
pfound = *iter;
successFlag = true;
break;
}
}
return successFlag;
}
}
I think this would work as well, and turns easier to follow. Trivial, but not so trivial when you write a couple thousand lines of code...
Comment by VS — December 3, 2007 @ 2:40 am
If only the single-exit vs multiple-exit point style debates were so straight-forward. They both make sense in different contexts but in many cases, yes, it is just a matter of style. Couple of examples where single-exit style is undesirable:
SESE forces an additional nested block, and hence an additional level of indentation too, where preconditions/arguments need to be checked before executing the actual implementation. SESE sometimes makes creation of temporary objects more inefficient than SEME would - e.g. returning string literal(only constructor called) vs returning a string object(constructor and assignment operator called). SESE doesn't make sense when there are multiple implicit exit points anyway - e.g. code with exceptions. You can say that recommending SESE as a rule or a standard for languages that support exceptions is obsolete - or at least over-emphasising it is.
I mostly prefer SEME, but the reason with regard to code in this specific post, though, was only to save a couple of lines.
Comment by tabrez — December 3, 2007 @ 8:21 pm
Dear Tabrez:
I am a total and I mean, total, newbie to Boost. It took me a major effort to get it running. I have...finally. Anyway, I need to traverse a large number of directories and their sub-directories. I am looking (Win platform) for files ending in a given extension (...as it happens, .php). Is there a quick way to match with a wild card or something similar so that I can get ALL files that are *.php?
Even a pointer to the correct documentation would be a big help.
Best,
P.
Comment by Petrus — December 13, 2007 @ 6:23 am
Hey P.,
I had the same question and I found the answer here:
http://blog.codedread.com/archives/2007/07/12/getting-to-know-c-next-filesystem/
Hope it helps, it'll sure help me out, gotta love the net!
M
Comment by Simon Blackhart — December 28, 2007 @ 8:52 am
I used the Boost Consulting installer. It would have helped to know that it needs internet access to run (this is not mentioned in the Getting Started withBoost documentation). However when I ran it on a machine with internet access it ran very smoothly and seemed to almost give me the libraries I need. Unfortunately I am using STLport and the linker searches for libraries containing '-p-'. These libraries were not downloaded and I can see no mention in the getting started documentation on how to build them. They were not built by bjam -sTOOLS=msvc stage
Comment by paul bates — January 5, 2008 @ 11:15 pm
I have written a shorter procedure to get started with Boost libraries under Visual Studio .NET IDEs using Boost Consulting installer:
http://beans.seartipy.com/2007/08/20/installing-c-boost-on-microsoft-windows-for-visual-studio-net-20032005/
It does register the fact that Internet connection is required ;)
Getting STLPort and C++ Boost to work together had been a great pain in the neck for me when I had last tried it(both on windows and gnu/linux). Will give it a try again(it needs to be built separately from Boost by the way, using Microsoft compiler).
Comment by tabrez — January 6, 2008 @ 11:40 pm
VS on December 3, 2007 at 2:40 am said:
Trivial, but not so trivial when you write a couple thousand lines of code...
a couple thousand lines of code in one function??? so, I'm not surprised of successFlag in your code ;)
Comment by J0ker — June 28, 2009 @ 11:40 am