C++/Hello, It's been a while
Expert: Ralph McArdell - 4/23/2005
QuestionHello, 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?
AnswerHello, 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.