65.9K
CodeProject 正在变化。 阅读更多。
Home

目录文件列表实用程序

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.28/5 (21投票s)

2009年1月14日

CPOL

3分钟阅读

viewsIcon

104593

downloadIcon

3803

一篇关于递归列出给定目录中所有文件的方法的文章。

引言

我遇到过无数次,日常使用的实用功能根本无法使用,程序员不得不反复编写和重写它们。 这是我系列文章中的第一篇,旨在提供一系列常用的函数,并提供示例应用程序说明如何使用它们,希望它们对众多开发人员有用,就像它们对我一样有用。

其中一个函数是获取所有文件(它们的绝对路径)的列表,给定一个起始目录,并可以选择递归搜索子目录,或者只输出给定目录下的文件。

我发现这在许多需要出于某种原因遍历每个文件的项目中非常有用。

背景

这个函数是多年前开发的,当时我偶然遇到了一系列需要读取给定目录中每个文件的项目,并且还要实时提供进度信息,例如,当前正在处理哪个文件、文件总数和已处理的文件数。 从那时起,我可能在几十个项目中使用过这个函数。

Using the Code

按照以下简单步骤在您的项目中使用该代码

  1. 将文件 Util.hUtil.cpp 添加到您的 Visual Studio C++ 项目(或任何其他类型的项目,我从未在 Visual Studio C++ 项目之外的任何其他项目中使用过该代码,因此任何关于在其他 C++ 项目中使用它的输入都非常感谢)。
  2. 在您打算使用此功能的 *.cpp 文件的顶部部分添加行 #include "Util.h"
  3. 声明一个 _tstring 的向量:vector <_tstring> vecstrFileList;
  4. 使用所需的参数调用静态函数 "GetFileList()"。
  5. 该向量现在将包含在指定目录中找到的所有文件路径,向量的每个元素都是一个字符串,表示文件的绝对路径。 请注意,在使用该向量之前,您必须显式地清除该向量,除非您希望该函数附加到该向量。 该函数也适用于网络驱动器目录路径。
  6. 查看源代码中的“获取文件列表”按钮事件处理程序函数,以了解步骤 1 到 5 的操作。
  7. 检查“Util.cpp”源文件或下面的代码片段,以详细描述该函数如何在内部工作,其中有大量注释。
void CUtil::GetFileList(const _tstring& strTargetDirectoryPath, 
     const _tstring& strWildCard, bool bLookInSubdirectories, 
     vector<_tstring>& vecstrFileList)
{
    // Check whether target directory string is empty
    if(strTargetDirectoryPath.compare(_T("")) == 0)
    {
        return;
    }

    // Remove "\\" if present at the end of the target directory
    // Then make a copy of it and use as the current search directory
    _tstring strCurrentDirectory = RemoveDirectoryEnding(strTargetDirectoryPath);

    // This data structure stores information about the file/folder
    // that is found by any of these Win32 API functions:
    // FindFirstFile, FindFirstFileEx, or FindNextFile function
    WIN32_FIND_DATA fdDesktop = {0};

    // Format and copy the current directory
    // Note the addition of the wildcard *.*, which represents all files
    // 
    // Below is a list of wildcards that you can use
    // * (asterisk)      - represents zero or more characters
    //                     at the current character position
    // ? (question mark) - represents a single character
    //
    // Modify this function so that the function can take in a search
    // pattern with wildcards and use it in the line
    // below to find for e.g. only *.mpg files
    //
    // "\\?\" prefix to the file path means that the
    // file system supports large paths/filenames
    _tstring strDesktopPath = _T("");
    strDesktopPath += _T("\\\\?\\");
    strDesktopPath += strCurrentDirectory;
    strDesktopPath = AddDirectoryEnding(strDesktopPath);

    if(strWildCard.compare(_T("")) == 0)
    {
        strDesktopPath += _T("*.*");
    }
    else
    {
        strDesktopPath += strWildCard;
    }

    // Finds the first file and populates the
    // WIN32_FIND_DATA data structure with its information
    // The return value is a search handle used in subsequent
    // calls to FindNextFile or FindClose functions
    HANDLE hDesktop = ::FindFirstFile(strDesktopPath.c_str(), &fdDesktop);    

    // If an invalid handle is returned by FindFirstFile function,
    // then the directory is empty, so nothing to do but quit
    if(hDesktop == INVALID_HANDLE_VALUE)
    {
        return;
    }

    // Do this on the first file found and repeat for every next
    // file found until all the required files
    // that match the search pattern are found
    do 
    {
        // Reconstruct the path
        _tstring strPath = _T("");
        strPath += strCurrentDirectory;
        strPath = AddDirectoryEnding(strPath);
        strPath += fdDesktop.cFileName;

        // Check if a directory was found
        if(fdDesktop.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            // Get the name of the directory
            _tstring strCurrentDirectoryName = GetDirectoryName(strPath);

            // If its a current (.) or previous (..)
            // directory indicator, just skip it
            if((strCurrentDirectoryName.compare(_T(".")) == 0) || 
               (strCurrentDirectoryName.compare(_T("..")) == 0))
            {
                continue;
            }
            // Other wise this is a sub-directory
            else
            {
                // Check whether function was called
                // to include sub-directories in the search
                if(bLookInSubdirectories)
                {
                    // If sub-directories are to be searched as well,
                    // recursively call the function again,
                    // with the target directory as the sub-directory
                    GetFileList(strPath, strWildCard, bLookInSubdirectories, 
                                vecstrFileList);
                }
            }
        }
        // A file was found
        else
        // if(fdDesktop.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)
        {
            // Add the string to the vector
            vecstrFileList.push_back(_tstring(strPath));
        }
    }
    // Search for the next file that matches the search pattern
    while(::FindNextFile(hDesktop, &fdDesktop) == TRUE);

    // Close the search handle
    ::FindClose(hDesktop);
}

关注点

这里提供的代码用于非托管 C++;如果您使用托管代码或 C#,它使用 .NET Framework,您可以使用 Directory.GetFiles 方法;您可以在 这里 阅读相关内容。

在编写此代码时,我发现,令我恼火的是,Microsoft Windows 没有任何易于使用的非托管 C++ 代码可以以直接而简单的方式执行此操作。 如果您发现此代码有用,请发表评论,这可能会让我心情愉快 微笑 | :)

已知问题

使用通配符在子目录中递归查找文件时,如果父目录中至少没有一个此类文件,则不会搜索嵌套目录。

历史

  • V1.0 - 初始版本。
  • V1.1 - 修复了递归子目录搜索。
© . All rights reserved.