递归获取文件:跳过无法读取的文件/目录?

8
我希望获取一个目录中的所有文件并将它们存储在一个数组中(包括子文件夹中的文件)。
string[] filePaths = Directory.GetFiles(@"c:\",SearchOption.AllDirectories);     

这种方法存在问题:如果出现异常,整个命令都会停止。有没有更好的方法可以做到,如果无法访问文件夹,则只是跳过该文件夹?

3
根据 @"c:\",我认为他是在询问方法调用是否能完成其工作并获取所有已授权访问的目录。 - hmqcnoesy
1
非重载 GetFiles 方法手动递归的例子 并添加异常处理不难。我 假设 问题出现在无法 读取 子目录上;而 Directory.GetFiles(.., without_recursive_search) 将按预期对特定目录进行全有或全无操作。 - user166390
尽管该示例很简单:结果仅是副作用,如果存在递归目录联接或链接,则会发生“坏事”。 - user166390
4个回答

7

如果您需要的话,可能需要自己多打一些字,编写像这个一样的目录遍历器:

    public static string[] FindAllFiles(string rootDir) {
        var pathsToSearch = new Queue<string>();
        var foundFiles = new List<string>();

        pathsToSearch.Enqueue(rootDir);

        while (pathsToSearch.Count > 0) {
            var dir = pathsToSearch.Dequeue();

            try {
                var files = Directory.GetFiles(dir);
                foreach (var file in Directory.GetFiles(dir)) {
                    foundFiles.Add(file);
                }

                foreach (var subDir in Directory.GetDirectories(dir)) {
                    pathsToSearch.Enqueue(subDir);
                }

            } catch (Exception /* TODO: catch correct exception */) {
                // Swallow.  Gulp!
            }
        }

        return foundFiles.ToArray();
    }

1
需要注意的事项:1)这是BFS实现,而不是DFS。2)这不会处理递归结构(NTFS中的连接点或硬/软链接)。3)ToArray似乎很愚蠢。 - user166390
1
@pst同意第2点和第3点(ToArray的作用是生成与问题中相同的string[],并不是必需的),但如果需要遍历所有可访问的子目录,第1点是否有很大的区别呢? - ikh
1
@ikh 我并不是说这样做有什么不好,只是它就是这样 :) 它的区别在于遍历顺序:C:\a\ C:\b\ C:\a\aa\ (广度优先)和 C:\a\ C:\a\aa\ C:\b\ (深度优先)——有些人可能会期望另一种(DFS)行为,所以需要注意。 - user166390
这个完美地运行了!这比内置版本慢得多 - 但当然它们不处理未授权的文件。 - Michael K

0

Directory.GetFiles无法跳过目录符号链接,这经常会导致循环然后抛出异常。

因此,基于@iks的答案和检查文件是真实的还是符号链接,这里提供了一个版本,类似于Directory.EnumerateFiles,可以即时提供结果:

    public static IEnumerable<string> FindAllFiles(string rootDir)
    {
        var pathsToSearch = new Queue<string>();


        pathsToSearch.Enqueue(rootDir);

        while (pathsToSearch.Count > 0)
        {
            var dir = pathsToSearch.Dequeue();
            var foundFiles = new List<string>();
            try
            {
                foreach (var file in Directory.GetFiles(dir))
                    foundFiles.Add(file);

                foreach (var subDir in Directory.GetDirectories(dir))
                {
                    //comment this if want to follow symbolic link
                    //or follow them conditionally
                    if (IsSymbolic(subDir)) continue;
                    pathsToSearch.Enqueue(subDir);
                }
            }
            catch (Exception) {//deal with exceptions here
            }
            foreach (var file in foundFiles) yield return file;
        } 


    }

    static private bool IsSymbolic(string path)
    {
        FileInfo pathInfo = new FileInfo(path);
        return pathInfo.Attributes.HasFlag(System.IO.FileAttributes.ReparsePoint);
    }

    static public void test()
    {
        string root = @"D:\root";
        foreach (var fn in FindAllFiles(root)
            .Where(x=>
            true    //filter condition here
            ))
        {
            Debug.WriteLine(fn);
        }
    }

ReparsePoint并不一定是符号链接!这会导致“误报”。要识别真正的符号链接,您必须使用本机API。完整的工作实现可以在此处找到:https://www.codeproject.com/Articles/15633/Manipulating-NTFS-Junction-Points-in-NET - marsh-wiggle

-1

试试这个:

DirectoryInfo directory = new DirectoryInfo(@"c:\");
        DirectoryInfo[] folders = directory.GetDirectories("*", SearchOption.AllDirectories);

        List<string> files = new List<string>();
        foreach (DirectoryInfo info in folders)
        {
            foreach (FileInfo file in info.GetFiles())
            {
                files.Add(file.Name);
            }
        }

1
如果有一个名为 C:\NoPermissionsToRead 的文件夹会发生什么? - user166390

-1

或者尝试这个:

        DirectoryInfo dirs = new DirectoryInfo(@"c:\");
        List<string> filenames = (from i in dirs.GetFiles("*", SearchOption.AllDirectories)
                                  select i.Name).ToList();

或者文件名不带扩展名:

        DirectoryInfo dirs = new DirectoryInfo@"c:\");
        List<string> filenames = (from i in dirs.GetFiles("*", SearchOption.AllDirectories)
                                  select System.IO.Path.GetFileNameWithoutExtension(i.Name)).ToList();

1
当目录无法读取时,这个程序会如何处理/响应? - user166390

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接