2

I need to perform a certain file operation in C++, like this:

  1. Find and delete all files in a given directory.
  2. Find if a given file exist, if yes, delete it, etc.

Please advise me a C++ solution/library which will work for both android and iOS platform.

user1908860
  • 509
  • 1
  • 9
  • 19
  • 1
    What's wrong with fopen()? – Shark Dec 29 '12 at 23:01
  • 1
    how to find all files in given directory using fopen() ? – user1908860 Dec 29 '12 at 23:05
  • You'll run into far more serious issues developing this game than finding files... luckily, Steven Lu gave you the solution. – Shark Dec 29 '12 at 23:08
  • why not writing such a library by yourself? Most people do that. – Sulthan Dec 29 '12 at 23:24
  • I appreciate your support @Shark but I must admit my class does a poor job in assisting in the task of finding and deleting all files in a dir. What you could use my class for, however, is reading out files whose paths you already know. I hope OP can pick out the difference. – Steven Lu Dec 29 '12 at 23:26

4 Answers4

1

C++ gives you provisions for file interaction.

I will show you my FileReader class which employs a bit of C style file handling using C++.

// BEGIN HEADER
#include <stdio.h>
#include <exception>
#include <stdexcept>

#define DISALLOW_COPY(type) \
    type(const type&); \
    void operator=(const type&)

class FileReader { // RAII applies to file contents
    FILE *file; // c-style open/close
    DISALLOW_COPY(FileReader);
protected:
    unsigned char *data; // local copy
    long size;
public:
    FileReader(const char *filename);
    ~FileReader();
    unsigned long getSize();
    unsigned char *getFileData();
};
// END HEADER

FileReader::FileReader(const char *filename) {

    file = NULL; data = NULL;
    if (!(file = fopen(filename, "rb"))) { throw std::runtime_error(std::string("File could not be opened: ")+filename); }
    fseek(file,0,SEEK_END);
    size = ftell(file);
    rewind(file);
    data = new unsigned char [size];
    VERIFY(size == (long)fread(data, 1, size, file)); // debug macro (just ignore it)
    fclose(file);
#ifdef DEBUG
    PRINT("FileReader opening file "); printf("%s, %ld bytes.\n",filename,size);
#endif
}
FileReader::~FileReader() {
    delete[] data;
}
unsigned char *FileReader::getFileData() { return data; }
unsigned long FileReader::getSize() { return size; }

I will note that you probably want to avoid using C++ to list files in the directory if you can. Why can you not simply assume whether or not certain files will be there or not be there? I've done a bit of game programming and pretty much the only times you need to worry about the filesystem are for logging purposes or for loading assets. For both of these you can pretty much just assume what their paths are.

In addition, you may want to look at the remove function for deleting files. I can't come up with any raw code in C++ for performing the task that ls is meant for. I wouldn't use C++ to do such a task (hint: ls is a pretty neat program).

Also take a look at stat and opendir (thanks Ben) which should be available on your platforms. Another point to make is that a task such as listing files in a dir are generally things you're gonna want to ask your OS kernel to do for you.

A more high-level approach mentioned by another answerer is Boost Filesystem, which is a solid choice as Boost usually is: Take a look at this directory iteration example.

From a game programming perspective I've tended to lean on stuff like Lua's os(). For example if you have a Python program you could just do something like os.system("ls") to get your dir contents assuming you have an ls program available.

You could also exec the ls program from your C++ program.

Steven Lu
  • 41,389
  • 58
  • 210
  • 364
  • Read the question, not just the title. – Ben Voigt Dec 29 '12 at 23:14
  • This is great. Saving into misc code snippets... @Ben: any C/C++ code on android would have to be called thru JNI as they are native methods, in iOS's Object-C however, that's not the case. But this looks very solid and reusable IMHO. – Shark Dec 29 '12 at 23:15
  • @BenVoigt You are probably right it doesnt answer the question directly. I had the code ready though, figured it would at least help tangentially. I think it also helps that you pointed out how my answer doesnt directly address some of the parts of the question (like delete all files in a dir...) – Steven Lu Dec 29 '12 at 23:16
  • If OP is capable, this will solve his problem. However, to make this really portable, I'd avoid classes and stick with structs. Other than that everything looks OK. – Shark Dec 29 '12 at 23:18
  • @Shark: This doesn't do a single thing the OP asked for, so how would it solve his problem? It doesn't enumerate files, it doesn't test a file for existence, and it doesn't delete files. The most solid and reusable code in the world is the wrong answer if it does the wrong thing. – Ben Voigt Dec 29 '12 at 23:20
  • Ok, it's getting better... `stat` is useful to discover if a file exists and `opendir` to enumerate files in a directory. But these aren't very portable. Should be available on both Android and iOS though. – Ben Voigt Dec 29 '12 at 23:22
  • http://www.gnu.org/software/libc/manual/html_node/Simple-Directory-Lister.html I figure dirent is new to you ;) – Shark Dec 29 '12 at 23:44
  • Yup looks like a `dirent` struct is what `readdir` fills up for you. – Steven Lu Dec 29 '12 at 23:49
  • Thanks a lot Steven for a prompt help and detailed explanation. So "dirent" can be used both iOS and Android platform, is it ? Meaning is dirent a cross platform solution ? – user1908860 Dec 30 '12 at 06:26
  • I have not tested that but `struct dirent` which you use `opendir` and `readdir` calls to populate should provide you with info direct from the kernel about your current directory. – Steven Lu Dec 30 '12 at 06:38
  • @user1908860 Take a look at this answer. http://stackoverflow.com/a/612176/340947 And don't forget to accept! – Steven Lu Dec 31 '12 at 03:59
1

You could try using the Boost library, which seems to be a popular choice. It runs on iOS and Android.

See this answer on SO for more info on how to use Boost to manipulate files and directories in a cross platform way.

For example, here's some code from the Boost website to check if a particular file exists or not, checking recursively in a directory:

#include "boost/filesystem/operations.hpp"
#include "boost/filesystem/path.hpp"
#include "boost/progress.hpp"
#include <iostream>

namespace fs = boost::filesystem;

int main( int argc, char* argv[] )
{
  boost::progress_timer t( std::clog );

  fs::path full_path( fs::initial_path<fs::path>() );

  if ( argc > 1 )
    full_path = fs::system_complete( fs::path( argv[1] ) );
  else
    std::cout << "\nusage:   simple_ls [path]" << std::endl;

  unsigned long file_count = 0;
  unsigned long dir_count = 0;
  unsigned long other_count = 0;
  unsigned long err_count = 0;

  if ( !fs::exists( full_path ) )
  {
    std::cout << "\nNot found: " << full_path.file_string() << std::endl;
    return 1;
  }

  if ( fs::is_directory( full_path ) )
  {
    std::cout << "\nIn directory: "
              << full_path.directory_string() << "\n\n";
    fs::directory_iterator end_iter;
    for ( fs::directory_iterator dir_itr( full_path );
          dir_itr != end_iter;
          ++dir_itr )
    {
      try
      {
        if ( fs::is_directory( dir_itr->status() ) )
        {
          ++dir_count;
          std::cout << dir_itr->path().filename() << " [directory]\n";
        }
        else if ( fs::is_regular_file( dir_itr->status() ) )
        {
          ++file_count;
          std::cout << dir_itr->path().filename() << "\n";
        }
        else
        {
          ++other_count;
          std::cout << dir_itr->path().filename() << " [other]\n";
        }

      }
      catch ( const std::exception & ex )
      {
        ++err_count;
                                                                                                                                                                        std::cout << dir_itr->path().filename() << " " << ex.what() << std::endl;
      }
    }
    std::cout << "\n" << file_count << " files\n"
              << dir_count << " directories\n"
              << other_count << " others\n"
              << err_count << " errors\n";
  }
  else // must be a file
  {
    std::cout << "\nFound: " << full_path.file_string() << "\n";    
  }
  return 0;
 }
Community
  • 1
  • 1
amrav
  • 307
  • 1
  • 9
1

Boost filesystem is fine for you. iOS version and clang++. Boost and NDK.

Naszta
  • 7,560
  • 2
  • 33
  • 49
0

IF you are working for Android, you are probably gonna be writing some of your game in the Java Activity and then delegate most of the heavy work to your native C/C++ code.

So, if Steven Lu's answer isn't working out of the box, you can use Java's File class and list all filenames under any directory, and then pass that to his native file handling class and get it over with. Or, just deal with it in Java.

For iOS though, maybe this will run?

#include <dirent.h> // directory header
#include <stdio.h>

void listdir (const char *path)
{
    // first off, we need to create a pointer to a directory
    DIR *pdir = NULL; 
    pdir = opendir (path); // "." will refer to the current directory
    struct dirent *pent = NULL;
    if (pdir == NULL) // if pdir wasn't initialised correctly
    { // print an error message and exit the program
        printf ("\nERROR! pdir could not be initialised correctly");
        return; // exit the function
    } // end if

    while (pent = readdir (pdir)) // while there is still something in the directory to list
    {
        if (pent == NULL) // if pent has not been initialised correctly
        { // print an error message, and exit the program
            printf ("\nERROR! pent could not be initialised correctly");
            return; // exit the function
        }
        // otherwise, it was initialised correctly. let's print it on the console:
        printf ("%s\n", pent->d_name);
    }

    // finally, let's close the directory
    closedir (pdir);
}


/** EXAMPLE USAGE **/
int main ()
{
    listdir ("C:\\");
    return 0;
}

But like I said, Android will give you bigger problems as you will need to request permissions to access the SD_CARD and INTERNAL_STORAGE if you want to mess with user's files in the AndroidManifest.xml file. Mostly because most of your stuff will be just in /data/data/your.package.name folder because you will have to ship your game as an APK and not a native app (an executable binary)

So, you don't really need an android and ios library, you need a library that can run on iOS which you will later wrap around with Android's Java (Activity) :)

Shark
  • 6,513
  • 3
  • 28
  • 50
  • Hey..thanks a lot for detailed explanation. So "dirent" can't be used for Andorid too with native c++, instead of java ? – user1908860 Dec 30 '12 at 06:20
  • dirent is pure C so it should be available under Android. But this is something much easier done in Java :) though, it might use the same method under the hood. – Shark Dec 30 '12 at 12:53