You are here:

C++/about !block.eof() and passing an arrayed varible through a function.

Advertisement


Question
I chaged this to

#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

// ...

using namespace std;

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 ReadDir(std::string path,    std::string ReadDirVar[256])  {
//   std::string ReadDirVar[256];
  std::cout << "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";
        ReadDirVar[i]=entries[i];
     }
  }
  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;
     }
  }

return 0;
//return &entries[0];
}

int main(){

string ReadDirVar[256];
//ReadDirVar[0]=ReadDir("/usr/home",);
ReadDir("/usr/home", ReadDirVar);
  //std::cout << *ReadDirVar[0] << *ReadDirVar[0];
cout << "[" << ReadDirVar[0] << "]" << "[" << ReadDirVar[1] << "]";
cout << ReadDirVar[2];
cout << ReadDirVar[2];
return 0;
}


so that i can use as a separate module.

Is there a way to get returned only folders?

instead of all the files too?




-------------------------
Followup To
Question -
Question 1)

I have the code:
#include <fstream.h>     
#include <iostream.h>
#include <math.h>
#include <stdio.h>
#include <string>
#include <stdlib.h>

using namespace std;

int main(){
string str;
char ch;

  string FilePath="tester";
  ifstream block(FilePath.c_str());
  if(!block){
     str="";
  }
  while (!block.eof()){
     str += block.get();
  }

cout << str << str;

return 0;
}

and in the file tester, contains a non-space alphebets of asdfasdf.

When I compile this source and run it, there are spaces and a new line involved.
I think that the .eof is causing the problems, is there a way to exclude those hideous characters in the end?  Oh and quotations, periods, commas and !@#$%^%&*() all these signs must be passed as well... Thank you Mr. McArdell


Question 2)
Remember the script that you have provided me the other week?

#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;
}

string ReadDir(string path)
{
string ReadDirVar[256];
//   std::string path;
  std::cout << "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";
        ReadDirVar[i]=entries[i];
     }
  }
  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;
     }
  }

return ReadDirVar[256];
}

int main(){

string ReadDirVar[256];
ReadDirVar[0]=ReadDir("/usr/home");
cout << ReadDirVar[0] << ReadDirVar[0];

return 0;
}


I have slightly modified this to my needs.
But I must somehow return an array value, I guess I'm going to have to use pointers?  Is there a way to pass the array values through a function?
This seems to compile but has a runtime error.

Thank you very much.
again.
Answer -
Question 1:

Well I am not 100% sure what is going on. Your question 1 code is a little flawed - if you do not manage to open the file tester then you effectively clear an already clear string but still go on to read the file! The inconsistent indentation implies you may have missed posting some construct such as a loop - in which case clearing a possibly previously used string would make more sense. Note you can clear a std::string using str.clear() or str.erase() (older standard C++ libraries may only support erase).

I would check your tester file - it may have trailing space and new lines in the file which you may not have noticed.

The std::istream::get() function will return the next character in the stream or the eof character which is usually -1. Note that because all character values in a char are used get returns a wider integer type to handle the EOF value, so this -1 is (for example) -1 as a 16 or 32 bit value not an 8 bit value. The exact type and value of EOF are defined by the std::ifstream::traits_type::int_type and the std::ifstream::traits_type::eof() function - not that because of the eof case get also returns std::ifstream::traits_type::int_type. In fact I think the traits_type is inherited from the std::basic_ios class template - in this case basic_ios<char>.

The problem with your code is that the stream does no know it is at the end of file until it tries to read at that point - its like not seeing a wall until you walk into it! So your code read on until it reaches the end of file whereupon get() returns the EOF value and your code adds this (truncated) EOF value to str - which is probably the character 255. What this looks like when displayed depends on your character set. When I tried your code I got a strange character at the end of str displayed in the debug output of MSVC++ .NET 2003 and a blank character displayed on the console which of course looked just like a space.

I revised your code as follows - demonstrating the use of the stream traits_type features:

int main()
{
       string str;
       string FilePath="tester";
       ifstream block(FilePath.c_str());
       if ( block )
       {
         for(;;)
         {
         std::ifstream::traits_type::int_type
         chr( block.get() );
         if ( chr ==
         std::ifstream::traits_type::eof()
         )
         {
         break;
         }
         str += static_cast<char>(chr);
         }
       }
       cout << str << str;
}

I broke a couple of statements across 2 or more lines as they wrapped in my text editor.

I now store the character from get into an object of std::ifstream::traits_type::int_type and break out of an infinite loop if it is EOF. Otherwise I append it to str. I explicitly cast the chr value to a char to keep my compiler from generating a warning about possible loss of data.

I test chr explicitly for EOF. I could have just checked the stream state at this point:

       if ( block.eof() ) ...

Finally I also prevented the stream being used if the file was not opened.

Question 2:

Now to the second question:

Your problem is probably the line:

       return ReadDirVar[256];

How big is ReadDirVar? 256 std::string elements. So what are that valid indices for such an array? 0 to 255.

You are returning the 257th element from an array of 256 elements - which explains the runtime error.

Now I do not see why you are using a built in array of strings in this function when elsewhere you are using std::vector< std::string >. If you make ReadDirVar (both of them) a std::vector< std::string > and push_back the directory name strings into it and return it then all should be well.

In fact on closer inspection I do not even see the need for ReadDir as it seems to be some sort of wrapper around dirfunct whose purpose is to convert a std::vector of std::string to a built in array of std::string - with no range checking to ensure that the entries vector does not contain _more_ than 256 strings.

If all you are trying to do is to access a std::vector as an array then try something like :

       &entries[0]

- it is a standard idiom to do this. What happens is that you obtain a reference to the first element and convert it to an address - i.e. a pointer - which can of course be used like a built in array. The memory used for the elements of a std::vector is guaranteed to be contiguous, just like a built in array.

If what you are trying to do is create a function that returns an array then do not bother - in C and C++ you cannot do this. Instead do as you do for dirfunct and pass the array as a parameter to be filled in:

       ReadDir(string path, string entries[256])
       {
       ...
       }

Or as you mention in the question, return (or pass in) a pointer to the array's first element (which for built in arrays is what using just the name of the array means) - but remember if the array is on the stack then it will have been destroyed _before_ you can copy its contents.

I would stick to using std::vector<std::string> and accessing its storage directly using the address of the first element idiom. Note that if you do return a std::vector from a function then it will be copied to the receiving std::vector - which means all its elements are copied. This is why it is more usual to see the vector passed in by reference as in dirfunct - it is more efficient, possibly much more efficient depending on the cost of copying elements and the number of elements that need to be copied.

Finally, if you are having to obtain names of items in directories then maybe the Boost (http://www.boost.org/) directory iterator would be of interest: http://www.boost.org/libs/filesystem/doc/index.htm .  

Answer
First could you please reduce the size of the posting I had difficulty finding your new question in all the source code, previous questions and answers!

Further note that this is not really a C++ question, it is a how to program UN*X like operating systems specifically their file systems question. I suggest you obtain a good book or three on programming for the system you are using UN*X, Linux, whatever. Get used to using the supplied help man pages, info, help etc.. for that system in question. Make use of the internet other than AllExperts the likes of Google and other search engines are usually a good place to find of such information you would not believe how many places have man page and other documentation accessible online.

Oh, and the term we use for code for C and C++ tends to be is source code and not script script implies something like shell script or perl.

Now, you cannot just have the directory entries in a directory returned to you (note: on UN*X and similar systems the term used in system API documentation is directory not folder). Instead you need to filter out those entries you are not interested in as you iterate through the directory entries.

If you wish to continue using the straight UN*X like system calls then the function group you need to look into are the stat functions (stat, fstat and lstat) you should be able to read the man pages for these on your local system by typing:

       man stat

at a shell prompt.

The idea is that for each entry in the directory you obtain information from stat on the entry and only add the entry to your collection if it is a directory.

You pass the full pathname for the file to the stat or lstat system functions together with a pointer to a struct stat object (this is a C API remember? So the documentation will refer to struct stat and the like as in C you need to specify struct before the name of the struct type when defining objects of a struct type, unlike C++ where the name of the struct type is all that is required.)

The difference between stat and lstat is in their handling of symbolic links. fstat differs in that you pass it an open file descriptor value instead of a pathname for the file.

I shall assume the use of stat from now on.

When stat returns successfully it will have filled in the fields (data members) of the stat struct whose pointer it was passed. The field we are interested in is the st_mode field which is of type mode_t which will most likely be an alias for some integer type.

The st_mode field will have various bits set to indicate various information about the file such as the owner, group, other permission values and useful for us the type of the file. The following type bits are defined (from the man page entry on my SuSE Linux box):

       S_IFMT     0170000   bitmask for the file type bitfields
       S_IFSOCK   0140000   socket
       S_IFLNK    0120000   symbolic link
       S_IFREG    0100000   regular file
       S_IFBLK    0060000   block device
       S_IFDIR    0040000   directory
       S_IFCHR    0020000   character device
       S_IFIFO    0010000   fifo

The man page also indicates that various macros are defined to help decode the st_mode value to answer the question Is this a xxx file type?:

       S_ISREG(m)  is it a regular file?
       S_ISDIR(m)  directory?
       S_ISCHR(m)  character device?
       S_ISBLK(m)  block device?
       S_ISFIFO(m) fifo?
       S_ISLNK(m)  symbolic link? (Not in POSIX.1-1996.)
       S_ISSOCK(m) socket? (Not in POSIX.1-1996.)

Note that the comments next to the last two seem to indicate that older systems may not support them.

Anyhow, for us the S_ISDIR(m) macro looks to be the thing to use to determine whether the current entry is a directory or not. So in pseudo code you need to modify your code thus:

       For each directory entry:
         Make full pathname of entry called  pathname
         Define stat object called entryStat
         Pass pathname and address of entryStat to stat function
         If S_ISDIR(entryStat.st_mode):
         Add to directory entries collection
         End If
       End For

Of course you can modify this a bit the names are an obvious thing to change.

The man page on stat indicates you need to include the following headers:

      #include <sys/types.h>
      #include <sys/stat.h>
      #include <unistd.h>

Note that I envision entryStat to be a local automatic variable, not a dynamically allocated item:

         stat entryStat;
         stat( pathname, &entryStat);

Now I have not shown error checking. stat can fail and so its return code should be checked. Like many UN*X like functions stat returns 0 on success and -1 on error and you check the global errno value to see what the error was:

       if ( 0 != stat(pathname, &entryStat) )
       {
       // check errno for specific error
       }

Or:
       if ( -1 == stat(pathname, &entryStat) )
       {
       // check errno for specific error
       }

Or even the terse:

       if (stat(pathname, &entryStat) )
       {
       // check errno for specific error
       }

Finally remember that pathname will be a C-style string so remember to call c_str() if your pathname string is a C++ std::string.

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.