【问题标题】:Getting files recursively: skip files/directories that cannot be read?递归获取文件:跳过无法读取的文件/目录?
【发布时间】:2012-08-13 01:00:18
【问题描述】:

我想获取数组中一个目录中的所有文件(包括子文件夹中的文件)

string[] filePaths = Directory.GetFiles(@"c:\",SearchOption.AllDirectories);     

这样做的问题是:如果抛出异常,整个命令就会停止。有没有更好的方法来做到这一点,这样如果一个文件夹无法访问,它就会跳过它?

【问题讨论】:

  • @"c:\" 来看,我认为他是在询问方法调用是否可以完成其工作并获取所有授予访问权限的目录。
  • 未重载的GetFiles method shows an example of recursing manually 并且向其添加异常处理并不难。我假设问题出现在无法读取子目录;并且Directory.GetFiles(.., without_recursive_search) 将按预期作为特定目录的全有或全无。
  • (虽然,这个例子很简单:结果只是副作用,如果存在递归目录连接或链接,就会发生“坏事”......)

标签: c# file exception directory


【解决方案1】:

你可能需要自己多做一些输入,然后编写一个像这样的目录遍历器:

    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) 这是 BFS 实现,而不是 DFS。 2) 这不会处理递归结构(NTFS 中的连接点或硬/软链接) 3) ToArray 看起来很傻。
  • @pst 同意第 2 点和第 3 点(ToArray 将产生与问题中相同的 string[] 并且完全没有必要),但如果所有可访问点,第 1 点是否会有很大不同子目录需要遍历吗?
  • @ikh 我并不是说它很糟糕,只是它是:) 这是按以下顺序遍历的区别:C:\a\ C:\b\ C:\a\aa\ (广度优先)和C:\a\ C:\a\aa\ C:\b\ (深度-first) -- 有些人可能期待其他 (DFS) 行为,因此请牢记这一点。
  • 完美运行!这比内置版本慢得多 - 但它们当然不处理未授权文件。
【解决方案2】:

Directory.GetFiles 不能跳过目录符号链接,这通常会导致循环然后出现异常。

因此,根据@iks 的回答和Check if a file is real or a symbolic link,这里有一个版本,可以像 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);
        }
    }

【讨论】:

  • A ReparsePoint 不一定是符号链接!这将得到“误报”。要识别真正的符号链接,您必须使用本机 API。可以在此处找到完整的工作实现:codeproject.com/Articles/15633/…
【解决方案3】:

试试这个:

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);
            }
        }

【讨论】:

  • 如果有文件夹C:\NoPermissionsToRead会怎样?
【解决方案4】:

或者试试这个:

        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();

【讨论】:

  • 这将如何处理/处理目录无法读取的情况?
猜你喜欢
  • 2021-03-10
  • 1970-01-01
  • 2011-09-27
  • 2018-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-18
相关资源
最近更新 更多