user638473 user638473 - 26 days ago 4
C++ Question

MFC Processing Files before Folders

Using MFC, I recursively iterate through a folder structure to get a list of files. This works great but when it comes to processing the files, I would like to process all the files in one folder first before going into subfolders. For example, If I have following hierarchy:

\A.txt
\B.txt
\C\C1.txt
\C\C2.txt
\D.txt
\E.txt
\F\F1.txt
\F\F2.txt
\F\F3\F4.txt
\F\F3\F5.txt
\F\F6.txt
\G.txt
\H.txt
\I.txt
...


I would like to process all the files in \ (A.txt, B.txt, D.txt, E.txt, etc.) before going into \C and everything in \F before going into \F\F3. But as I collect these files into a std::vector, the vector is being populated exactly as the list appears above. I suspect this is because of some internal ordering of the Windows OS. Then when I iterate through the vector and output a message when the "path" part changes, I get something similar to the following:

Processing files in \
Processing files in \C
Processing files in \
Processing files in \F
Processing files in \F\F3
Processing files in \F
Processing files in \


This makes it appear as if the processing is jumping back and forth between folders when I would prefer something like:

Processing files in \
Processing files in \C
Processing files in \F
Processing files in \F\F3
...


and to process all the files of a complete hierarchy before moving on to a sibling folder. Essentially, the messages should appear in alphabetical order based on the folder structure being used.

I thought of using CFileFind::IsDirectory to keep a list of folders until the entire folder has been iterated through and then going into each saved folder name list and iterating through it (and saving its own folder list before moving on, etc.), but that seems like overkill. There's got to be a better way.

My current code is similar to the following, where iFound is a count of ALL the files in the complete hierarchy:

void GetFiles(CString& strSrcFolder, int& iFound)
{
CString strFileSpec(strSrcFolder);

if (strFileSpec.Right(1) != _T("\\"))
strFileSpec += "\\";

strFileSpec += _T("*.*");

CFileFind Search;
BOOL bFound = Search.FindFile(strFileSpec);

while (bFound) {
bFound = Search.FindNextFile();

if (Search.IsDots())
continue;

if (Search.IsDirectory()) {
if (m_oPrefs.Recurse())
GetFiles(Search.GetFilePath(), iFound);
else
continue;
}
else {
m_vFiles.push_back(Search.GetFilePath());
++iFound;
}
}

Search.Close();
}


I hope I've made what I'd like to accomplish easy to understand, but don't hesitate to ask if any further clarification is needed. TIA.

Answer Source

From your description your actual pseudocode is like this:

ProcessDirectory(dir)
{
  for all items in dir
  {
    if item is a file
    {
      ProcessFile(item)
    }
    else 
    {  // it's not a file, so it's a directory
      ProcessDirectory(dir)
    }
  }
}

But you need this:

ProcessDirectory(dir)
{
  list directorylist

  for all items in dir
  {
    if item is a file
    {
      ProcessFile(item)
    }
    else 
    {  // it's not a file, so it's a directory
      add item to list
    }
  }

  // now all files of the current directory (dir) have been processed

  // now let's process the directories of the current directory (dir)
  for all items in list
  {
    ProcessDirectory(item) 
  }
}

As you can see, it's pretty much the same apart from the list. This is absolutely not overkill and I don't see any simpler method.

For the list you can use for example the CStringArray MFC class.

Another method would be to derive a your own class from CFindFile that ensures the directories come after the files during iteration.