You are here:

C++/Hello, It's been a while

Advertisement


Question
Hello, i dunno if you remember who i am. lol.
But as you know your my favorate, c++ master.

Ok, i guess this question can be categorized as file handling?
I would like to know if there is a way to get all the files & folders that exists in a folder, and save those values into a variable.

for instance...
inside of the folder /root
I have the following folders
/root/jack
/root/james
/root/ralph
/root/justin

and somehow, I would read those values jack james ralph and justin, and save it in something like...
Root_Values[0]=jack
Root_Values[1]=james etc..

I don't know if this is possible, but i've definately seen softwares that has done this.
So it must be possible, and I was wondering if I would have to use the system function
system(ls | command);
or something... where when i ls, to list...
somehow i can capture the outcomes of ls, and save that into a variable.

Is there a better method?  and is that method appropriate?

Answer
Hello, thanks for the vote of confidence!

Now, as to your question. I am not sure what you wish to do with the file and directory information - you say "a way to get all the files & folders that exists in a folder, and save those values into a variable."

What do you mean by getting all the files and folders and what does it mean to save them to a variable?

I shall prefer the term directory to folder from here on.

The example you show is not achievable in C++ - as it requires creating object (i.e. variable) names on the fly at runtime and C++ is a statically typed and linked language - which means you have to define all named C++ entities (objects, types, functions etc..) at compile time - i.e. when you write the code. You can create objects at runtime using new of course but you have to assign it to something with a name - e.g. a named pointer to the type of the object.

Maybe you did not mean to imply that variable names are created from the names of the directory members but this is what you showed:


Root_Values[0]=jack

(note you missed the terminating semicolon:

Root_Values[0]=jack;

)

here jack has to be known at compile time whereas your scheme would only have the name jack determined at runtime - if you try reading the contents of another directory maybe you would have to use:

Root_Values[0]=file_0001.dat;
Root_Values[1]=file-0002.dat;
Root_Values[0]=My MS Word Document.doc;

Note the use of characters that are _not_ valid in C++ names - spaces, dots, hyphens etc. as C++ reserves them for other uses.

Ignoring the handling of illegal C++ identifier characters for the moment this is what you would need to do - in pseudo code - to create a variable jack at runtime:

dirEntry = GetNextDirectoryEntry();
if ( dirEntry is a directory )
{
       CreateDirectoryVariable( dirEntry.name );
}
else // assume entry is a file:
{
       CreateFileVariable( dirEntry.name );
}

Assume dirEntry is some type that contains information about the directory entry - specifically for our purposes its name and whether it refers to a file or directory.

The two functions CreateDirectoryVariable and CreateFileVariable cannot be written in C++ as C++ has not facilities to allow dynamically creating objects with names determined at runtime, unlike languages like Java and the .NET languages like C#.

So your example is _not_ practical in C++.

The usual technique - and one you imply at the end of your question - is to obtain the names of the items in a directory and use them to open a set of files or display to the user - who then selects the item(s) they wish and the appropriate action is taken - e.g. one or more file selections may open those files in an editor application, whereas a directory selection would cause the items in that directory to be displayed for selection. If you think about ls has to do this and ls is written in C. In fact you should be able to get the source code to various ls implementations quite easily - Linux source is easily available for example.

The bad news is that there is no standard way in C or C++ to get the information about the contents of directories. You have to resort to operating system specific API function calls - so for example code you write for UN*X like operating systems (including Linux) will not build on MS Windows operating systems as they use a different set of API functions. And on some platforms there may be no concept of directories or folders at all - which is probably why there is no standard language library support for them.

The good news is that there are libraries available that help. One such library can be found in the Boost libraries - see http://www.boost.org/ and http://www.boost.org/libs/filesystem/doc/index.htm in particular, which has classes for pathnames and directory iteration, the documentation contains some example code.

If you wish to try it raw then on UN*X like systems - which I assume you are using as you mention ls - then you use the xxxdir functions and the DIR and dirent structs - remember such operating systems have C APIs, not C++ APIs. Look up the following in the man pages on your system: opendir, closedir, readdir and rewinddir. The idea is that you open a directory, then read entries from the directory using readdir - which returns a pointer to a dirent struct which contains a name field which contains the name as a char * C-style string. This value is overwritten each time readdir is called so you have to copy the name string returned each time if you wish to collect them all. readdir returns NULL when there are no more entries in the directory. When you are done you call closedir to close the directory:

#include <sys/types.h>        // UN*X like system header
#include <dirent.h>          // UN*X like system header
#include <errno.h>          // UN*X like system header

#include <vector>          // C++ standard library header
#include <string>          // C++ standard library header
#include <iostream>          // C++ standard library header

// ...

int dirfunct
( std::string const & path
, std::vector<std::string> & dirEntryNames
)
{
       DIR * dir = opendir( path.c_str() );
       if ( dir != NULL )
       {
         dirent * entry( NULL );
         do
         {
         entry = readdir( dir );
         if ( entry != NULL )
         {
         // save _copy_ of returned entry name
         dirEntryNames.push_back( entry->d_name );
         }
         }
         while ( entry!= NULL );

         if ( closedir( dir ) )
         {
         // closedir failed
         return -1;
         }

       }
       else // opendir returned NULL
       {
         return -1;
       }
       return 0;
}

int main()
{
         std::string path;
       std::cout << "Path: ";
       std::cin >> path;
       std::vector<std::string> entries;
       if ( !dirfunct(path, entries) )
       {
         std::cout << "Contents of path:\n";
         for ( int i=0; i < entries.size(); ++i )
         {
         std::cout << entries[i] << "\n";
         }
       }
       else
       {
         switch (errno)
         {
         case EBADF:
         std::cout << "Invalid directory stream descriptor.\n";
         break;
         case EACCES:
         std::cout << "Permission denied.\n";
         break;
         case EMFILE:
         std::cout << "Too many file descriptors in use by process.\n";
         break;
         case ENFILE:
         std::cout << "Too many files are currently open in the system.\n";
         break;
         case ENOENT:
         std::cout << "Entered path name does  not  exist, or is an empty string.\n";
         break;
         case ENOMEM:
         std::cout << "Insufficient memory to complete the operation.\n";
         break;
         case ENOTDIR:
         std::cout << "Entered path is not a directory.\n";
         break;
         default:
         std::cout << "Unexpected error, code: " << errno << "\n";
         break;          
         }
       }
}

The above code shows the idea - you include some operating system files, and I choose to save the entry names in a std::vector of std::strings - I try _not_ to use raw built in arrays / C-style strings where possible. In some function dirfunct here - you would open the directory and if successful extract and copy the names of the directory entries, in this case pushing them onto the end of the vector as std::strings. Finally the directory is closed.

I have tried to show the handling the failures of opendir and closedir - if readir fails then it returns NULL and we stop and try closing the directory which would most likely fail for the same reason. The code could be tidier in this respect but I think this structure shows you how to test for each function's failure.

Note that closedir returns 0 on _success_ and -1 on failure so the test logic looks backwards - this is common for UN*X like system calls, and I have dirfunct do likewise. UN*X and related systems use a global variable called errno to store the last error code which should be checked if a call indicates failure to determine the nature of the failure - the errno values you can expect to be set by a function are typically detailed in the man page documentation for the function.

I did not bother in this example with showing exception handling for the use of the vector.

I exercise dirfunct in main it asks the user for a path name and passes it and an empty vector of strings to dirfunct, testing the returned value from dirfunct. If all is well and dirfunct returns 0 then each collected entry name is printed to the console.

In the case of an error (dirfunct returns -1) then an error message is printed to the console based on the value of errno.

Note that the memory required for the std::strings and std::vector is totally managed by these types so I do nothave to remember to cleanup anything.

I built and ran the example program under SuSE Linux 7.1 using g++ 2.95.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.