C++ Boost
C++ Boost Filesystem Library(Part III): Example Programs
8My 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.
[cpp]
// 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;
}
[/cpp]
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.
[cpp]
void sls(const bfs::path & p)
{
unsigned long fc=0, dc=0;
if( !bfs::exists(p) )
std::cout<<"\nFile Not Found:"<
else if( !bfs::is_directory(p) )
std::cout<<"\nFound: " << p.native_file_string() << "\n";
std::cout<<"In directory:"<
for(; iter != end_iter; ++iter)
{
try {
if(bfs::is_directory(*iter))
{
++dc;
std::cout<
}
else
{
++fc;
std::cout<
}
} catch(const std::exception & ex) {
std::cout<
}
std::cout<
} //sls
[/cpp]
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.
C++ Boost Filesystem Library(Part II): Example Programs
11
Continuing from where I had left in my earlier post containing the basics of the C++ Boost Filesystem Library, below are some example programs that make use of some common facilities available in the Boost Filesystem Library. I assume that all the code snippets shown in this post are properly nested within the main() function apart from including the following things:
[cpp]
#include
#include
#include
namespace bfs=boost::filesystem;
[/cpp]
1. Simple program to demonstrate the use of file creation and removal operations:
[cpp]
std::cout<<"Enter your choice:\n";
std::cout<<"1. Create folder\n2. Rename File\n3. Remove File\n4. Copy File\n";
char ch;
std::cin>>ch;
std::string name, new_name;
switch(ch)
{
case ’1′:
std::cout<<"Enter folder name:";
std::cin>>name;
bfs::create_directory(bfs::path(name));
break;
case ’2′:
std::cout<<"Enter file name:";
std::cin>>name;
std::cout<<"Enter new name:";
std::cin>>new_name;
bfs::rename(name, new_name);
break;
case ’3′:
std::cout<<"Enter file name:";
std::cin>>name;
bfs::remove(bfs::path(name));
break;
case ’4′:
std::cout<<"Enter file name:";
std::cin>>name;
std::cout<<"Enter new name:";
std::cin>>new_name;
bfs::copy_file(name, new_name);
break;
}
std::cout<<"Operation finished."<
[/cpp]
Pay attention to the following four Boost Filesystem functions used:
[cpp]
bfs::create_directory(bfs::path(name));
bfs::rename(name, new_name);
bfs::remove(bfs::path(name));
bfs::copy_file(name, new_name);
[/cpp]
They all do what their function names suggest. Like its always the case with the simple example programs, no error checking is done here ;)
2. Removing all the files from a directory:
[cpp]
std::cout<<"Enter the name of the folder to empty:";
std::string name;
std::cin>>name;
bfs::remove_all(bfs::path(name));
std::cout<<"Operation completed."<
The other examples needs an introduction to the directory_iterator class which is used to iterate over the contents of a directory. A simple usage looks like this:
[cpp]
bfs::path p(“folder”);
bfs::directory_iterator dir_iter(p), dir_end;
for(;dir_iter != dir_end; ++dir_iter)
{
std::cout<<(*dir_iter).leaf();
}
[/cpp]
A directory_iterator object can be created by passing it a name of a directory. Applying the prefix increment operator(++) to it makes it point to the next file in the directory. Applying the indirection operator(*) returns the file currently being pointed to, as a ‘path’ object. We call the leaf() method on the returned ‘path’ object to print the file name. Using ‘dir_iter->leaf()’ instead of ‘(*dir_iter).leaf()’ has the same effect.
Using a ‘directory_iter’ object, we can create a function similar to the ‘remove_all()’ function that we used in Example 2.
3. Removing all the files from a directory by iteration:
[cpp]
std::cout<<"Enter the name of the folder to empty:";
std::string name;
std::cin>>name;
bfs::path p(name);
if(!bfs::exists(p) || !bfs::is_directory(p))
{
std::cout<<"Invalid input."<
}
bfs::directory_iterator dir_iter(p), dir_end;
for(;dir_iter != dir_end; ++dir_iter)
{
std::cout<<"Removing file: "<
bfs::remove(*dir_iter);
}
std::cout<<"Operation Completed."<
Simple error checking is done on the input before calling the remove()function on all the files present in the specified folder. Will follow up with more examples using the Boost Filesystem Library.
Boost Filesystem Library: Writing Portable C++ Programs to Acess The Filesystem
5
Boost Filesystem library allows us to write portable code to access files and directories from a C++ program without using the operating system specific system/library calls. I have written about the Boost family of libraries in an earlier post, and have posted the procedure to install and get started with the Boost libraries on various OS distributions here. In this post, I will be talking about one of the popular Boost libraries – the Filesystem library. Lets first consider how a C++ program that needs to check for the existence of a file might look like:
first.cpp
[cpp]
#include
#include
#include
int main()
{
struct stat st;
if(lstat(“first.cppâ€, &st) == -1 && errno == ENOENT)
std::cout<<"the file doesn't exist.\n";
else
std::cout<<"the file exists.\n";
}
[/cpp]
The above code runs under most of the GNU/Linux operating systems. It won’t work under the Windows operating system though. One way to write portable code that runs under different operating systems without needing any modifications, is to use #ifdef family of preprocessor directives that selectively include different parts of the code on different operating systems. A better way would be to create an intermediate abstraction layer that provides a single interface to the programs to talk to different operating systems. Boost provides one such interface, with implementations available for Windows and all POSIX compatible operating systems, which covers most of the popular operating systems today. Additional benefits of using the Boost libraries are robustness and the design practices that are close to philosophy of the C++ standard library itself. Let’s try to write the above program using the Boost Filesystem library:
first.cpp
[cpp]
#include
#include
namespace bf = boost::filesystem; //create an alias
int main()
{
bf::path p(“first.cpp”);
if(bf::exists(p))
std::cout<
std::cout<
[/cpp]
Line 4 above creates an alias named ‘bf’ to the namespace ‘boost::filesystem’ which contains all the functions useful for the manipulation of files(like exists(), is_directory(), etc). ‘path’ is a type that allows paths to be represented in a platform independent format; instead of passing path names as strings to every boost function, we just wrap it in a ‘path’ object and use the object in its place.
[cpp]
bf::path p(“first.cpp”); //’p’ represents “first.cpp”
[/cpp]
The function exists() takes a path name as its argument(i.e, an object of type ‘path’) and returns true if the path represented by the path name exists; otherwise false. We print the return value of the function ‘leaf()’, which is the last part of the path stored in p; so for example if “/home/user/tests/first.cpp” is stored in the path object, then invoking leaf() on it will return ‘first.cpp’.
The exact procedure to compile the above program depends on the specific platform used(compilation on some platforms discussed here), but it should be on the similar lines to :
Similarly, there are a bunch of other operations that can be performed on the underlying file system using the boost filesystem library, and all of these are declared in the ‘operations.hpp’ header file. The class ‘path’ itself is defined in the ‘path.hpp’ header file, but we do not need to include it separately as it is already included by the ‘operations.hpp’ file. Some of the other interesting functions available in boost filesystem library are demonstrated by the following example:
second.cpp
[cpp]
#include
#include
namespace bf = boost::filesystem;
int main()
{
bf::path p(“second.cpp”);
if( !bf::exists(p) )
{
std::cout<
}
if( bf::is_directory(p) )
std::cout<
std::cout<
std::cout<
std::cout<<"file size: "<
[/cpp]
The program should be self-explanatory. There are a lot of other functions that allow operations like: creation and deletion of files, iterating over all the files present in a directory, copying and moving files between directories, etc. I would follow-up with examples that use these operations, but until then the following links should keep you busy:
Boost Filesystem
operations.hpp header file
Installing C++ Boost on SuSE and Fedora
0C++ Boost on SuSE:
Its simple to install C++ Boost on a SuSE system by using its YaST package manager.
- Run YaST and select "Software" from the left pane. Now select "Software Management" from the right pane and search for "boost" using the Search box.
- Select the boost packages that you want to install from the right pane(you atleast need boost - 1.33.0-3) and press the "Accept" button.
You can now test the boost installation by compiling the two programs I had mentioned in my earlier post, using the following commands:
sh# g++ -o first first.cpp
sh# g++-o second second.cpp -lboost_filesystem
C++ Boost on Fedora:
Installing C++ Boost on RedHat Fedora systems is also along the same lines. First download the boost-1.33.1-5.i386.rpm RPM file from the internet. you can search for a different version of the file if you are not using the Fedora Core 5 system. Install it using the following command(you can directly click on the file):
sh# rpm -ivh boost-1.33.1-5.i386.rpm
You can also install it using the yum package manager of Fedora. Test the installation just as mentioned in the case of SuSE.
Installing C++ Boost on Slackware/Zenwalk
9C++ Boost 1.33.1 on Slackware/Zenwalk:
- Download bjam slackware package(tgz) and boost-1_33_1 source package(tar.gz) from the sourceforge.net website:
bjam for slackware
c++ boost source package
(or Google for the latest versions of these two packages) - Save these two files in the home directory and then cd into it:
sh# cd $HOME
- Install bjam(as root):
sh# installpkg boost-jam-3.1.11-1-linuxx86.tgz
sh# export PATH=$PATH:/boost-jam-3.1.11-1-linuxx86/ - Compile and install boost(as root):
sh# tar xjvf boost_1_33_1.tar.bz2
sh# cd boost_1_33_1
sh# bjam “-sTOOLS=gcc” install
C++ Boost is now installed. This procedure doesn’t install the files in the default library paths, so compilation commands are slightly longer than they are for Gentoo and Debian/Ubuntu:
sh# g++ -o first first.cpp -I/usr/local/include/boost-1_33_1
sh# g++ -o second second.cpp -I/usr/local/include/boost-1_33_1 -L/usr/local/lib -lboost_filesystem-gcc
You can simplify this by adding the paths of boost folders to gcc library environment variables:
sh# export LIBRARY_PATH=/usr/local/lib
The compilation can now be performed using the following simple commands:
sh# g++ -o second second.cpp -lboost_filesystem-gcc
The two ‘export’ commands can be added at the end of the profile file(/etc/profile or $HOME/.bash_profile) to avoid having to type them every time a new shell is opened. Other way to simplify the process would be to create symbolic links to boost library paths in the Slackware standard include paths so that gcc can find them directly.
Recent Comments