4.2.2.  QDir, QFileInfo, QDirIterator

[ fromfile: iterators.xml id: qdiriterator ]

A directory, sometimes called a folder, is a container for files. Because a directory can also contain other directories, it has a natural tree structure. A directory can also contain symbolic links (called symlinks) which point to other files or directories. A symlink is a reference that can be used instead of the name or path for most operations that handle files or directories.

Qt provides several platform-independent ways to traverse a directory tree. The classes QDir and QFileInfo enable you to obtain a list of the contents of a directory and then get information about each entry. Example 4.2 shows a recursive function that uses these classes to visit selected items in a directory. It can determine whether an item is a file, a directory, or a symlink and, depending on the parameter choices, process the item appropriately. The first parameter identifies the directory that is to be traversed. The second parameter determines whether the function should recursively descend into any subdirectories that it finds in the directory. The third parameter determines whether the function should process the symlinks that it finds in the directory. The main goal of this particular function is to locate "mp3" files and add their paths to a list.

Example 4.2. src/iteration/whyiterator/recurseadddir.cpp

[ . . . . ]
void recurseAddDir(QDir d, bool recursive=true, bool symlinks=false ) {
    d.setSorting( QDir::Name );
    QDir::Filters df = QDir::Files | QDir::NoDotAndDotDot;
    if (recursive) df |= QDir::Dirs;
    if (not symlinks) df |= QDir::NoSymLinks;
    QStringList qsl = d.entryList(df, QDir::Name | QDir::DirsFirst);
    foreach (const QString &entry, qsl) {
        QFileInfo finfo(d, entry);
        if ( finfo.isDir() ) {
            QDir sd(finfo.absoluteFilePath());
            recurseAddDir(sd);
        } else {
            if (finfo.completeSuffix()=="mp3")
                addMp3File(finfo.absoluteFilePath()); 1
        }
    }
}
[ . . . . ]

1

Nonreusable part

<include src="src/iteration/whyiterator/recurseadddir.cpp" href="src/iteration/whyiterator/recurseadddir.cpp" id="recurseadddir.cpp" mode="cpp"/>


The application listed in Example 4.3 reuses QDirIterator to accomplish the same thing with fewer lines of code.

Example 4.3. src/iteration/diriterator/diriterator.cpp

[ . . . . ]
int main (int argc, char* argv[]) {
    QCoreApplication app(argc, argv);
    QDir dir = QDir::current();
    if (app.arguments().size() > 1) {
        dir = app.arguments()[1];
    }
    if (!dir.exists()) {
        cerr << dir.path() << " does not exist!" << endl;
        usage(); 
        return -1;
    }
    QDirIterator qdi(dir.absolutePath(),
            QStringList() << "*.mp3",
            QDir::NoSymLinks | QDir::Files,
            QDirIterator::Subdirectories );
    while (qdi.hasNext()) {
        addMp3File(qdi.next());
    }
}
[ . . . . ]

<include src="src/iteration/diriterator/diriterator.cpp" href="src/iteration/diriterator/diriterator.cpp" id="diriterator-example.cpp" mode="cpp"/>


There is an important difference between the two applications. In Example 4.2, the call to the project-specific function, addMp3File() occurs inside the definition of the function, recurseAddDir(), that manages the iteration, severely limiting the reusability of that function. In Example 4.3, QDirIterator manages the iteration. The call to addMp3File() occurs in the client code for QDirIterator (main()) and, thus, can have no effect on the reusability of that class. Using the Iterator pattern in appropriate places can make your code much simpler and easier to reuse.