52

I want to recursively scan a directory and all its sub-directories for files with a given extension - for example, all *.jpg files. How can you do that in Qt?

sashoalm
  • 75,001
  • 122
  • 434
  • 781
Dimse
  • 1,476
  • 1
  • 10
  • 15

4 Answers4

105

I suggest you have a look at QDirIterator.

QDirIterator it(dir, QStringList() << "*.jpg", QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext())
    qDebug() << it.next();

You could simply use QDir::entryList() recursively, but QDirIterator is simpler. Also, if you happen to have directories with a huge amount of files, you'd get pretty large lists from QDir::entryList(), which may not be good on small embedded devices.

Example (dir is QDir::currentPath()):

luca @ ~/it_test - [] $ tree
.
├── dir1
│   ├── image2.jpg
│   └── image3.jpg
├── dir2
│   └── image4.png
├── dir3
│   └── image5.jpg
└── image1.jpg

3 directories, 5 files
luca @ ~/it_test - [] $ /path/to/app
"/home/luca/it_test/image1.jpg"
"/home/luca/it_test/dir3/image5.jpg"
"/home/luca/it_test/dir1/image2.jpg"
"/home/luca/it_test/dir1/image3.jpg"
Luca Carlon
  • 9,546
  • 13
  • 59
  • 91
  • 12
    I wasted a lot of time because of not reading official docs properly. Please note "After construction, the iterator is located before the first directory entry. The `next()` function returns the path to the next directory entry and advances the iterator." So your code that deals with files should come after `it.next()`. – Andrew-Dufresne Dec 22 '15 at 18:18
  • I see this block of code all over, but it doesn't work. It finds all files matching the glob pattern at the top level of the directory tree only, and does not descend to lower levels. – SixDegrees Feb 21 '20 at 11:23
  • @SixDegrees not sure what you mean. I added an example. – Luca Carlon Sep 26 '21 at 20:22
9

This should work :

void scanDir(QDir dir)
{
    dir.setNameFilters(QStringList("*.nut"));
    dir.setFilter(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks);

    qDebug() << "Scanning: " << dir.path();

    QStringList fileList = dir.entryList();
    for (int i=0; i<fileList.count(); i++)
    {
        if(fileList[i] != "main.nut" &&
           fileList[i] != "info.nut")
        {
            qDebug() << "Found file: " << fileList[i];
        }
    }

    dir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
    QStringList dirList = dir.entryList();
    for (int i=0; i<dirList.size(); ++i)
    {
        QString newPath = QString("%1/%2").arg(dir.absolutePath()).arg(dirList.at(i));
        scanDir(QDir(newPath));
    }
}

The differences from your code are the following:

  • Breadth first search instead of depth first search (no reason for it, I just prefer it)
  • More filters in order to avoid sym links
  • EntryList instead of EntryInfoList. You don t need if you just want the name of the file.

I tested it and it works correctly, but notice the following:

  • This may take a lot of time, so consider running it from thread
  • If there is deep recursion you may have problem with your stack
pnezis
  • 12,023
  • 2
  • 38
  • 38
  • Unless I'm missing something, you changed the output of his function. He wants the list of all the files in the given directory, while your function will only print the file names but won't provide the list. – laurent Nov 08 '11 at 16:24
  • He wasn't filling the `nutFiles` string list, so I didn't included this part. It's pretty easy though to modify the function and get the list of all files – pnezis Nov 08 '11 at 16:28
4

I used QDirIterator.

Here's how I do it and how simple it was to find all XML absolute file paths recursively very fast (Qt4.8.1):

// used to store the file paths
filesStack = new QStack<QString>();

// I use a file dialog to let the user choose the root folder to search in
if (fileDialog->exec() == QFileDialog::Accepted) {
    QDir selectedDir(fileDialog->selectedFiles().first());
    selectedDir.setFilter(QDir::Files |
                          QDir::Dirs | QDir::NoDot | QDir::NoDotDot);
    QStringList qsl; qsl.append("*.xml"); // I only want XML files
    selectedDir.setNameFilters(qsl);
    findFilesRecursively(selectedDir);
}

// this function stores the absolute paths of each file in a QVector
void findFilesRecursively(QDir rootDir) {
    QDirIterator it(rootDir, QDirIterator::Subdirectories);
    while(it.hasNext()) {
        filesStack->push(it.next());
    }
}

Thanks to everyone for the hints.

EDIT: I may have omitted some declarations, beware.

Paul-Sebastian Manole
  • 2,538
  • 1
  • 32
  • 33
0

Another sample which indexes all files, using QFileInfo:

void ID3Tab::scanDir( QDir dir )
{ QFileInfoList fil = dir.entryInfoList( QStringList( "*.mp3" ),
                                         QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks,
                                         QDir::Name | QDir::IgnoreCase );
  foreach ( QFileInfo fi, fil )
    indexFile( fi );

  QFileInfoList dil = dir.entryInfoList( QStringList( "*" ),
                                         QDir::AllDirs | QDir::NoDotAndDotDot | QDir::NoSymLinks,
                                         QDir::Name | QDir::IgnoreCase );
  foreach ( QFileInfo di, dil )
    scanDir( QDir( di.absoluteFilePath() ) );
}
MangoCat
  • 89
  • 5