不能't扫描带有空格和$等特殊字符的文件夹

菲霍维

成员
已加入
2019年11月26日
留言内容
7
编程经验
Beginner
你好,

我想扫描应用程序中的驱动器,但是无法扫描$ Recyclebin和名称中带有空格的文件夹。

对于这些错误,我找不到任何解决方案,我试图找到NuGet软件包来为我解决此问题。
我正在使用System.IO

Program.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace FileHandler
{
    class Program
    {
        private static void Main(string[] args)
        {
            GetAllFilesFromFolder(@"E:\", true);
        }

        private static List<string> GetAllFilesFromFolder(string root, bool searchSubfolders)
        {
            Queue<string> folders = new Queue<string>();
            List<string> folderCount = new List<string>();
            List<string> files = new List<string>();
            folders.Enqueue(root);
            while (folders.Count != 0){
                string currentFolder = folders.Dequeue();
                try {
                    string[] filesInCurrent = Directory.GetFiles(currentFolder, "*.*", System.IO.SearchOption.TopDirectoryOnly);
                    files.AddRange(filesInCurrent);
                }
                catch
                {
                    //Console.WriteLine("Error: " + currentFolder);
                    // Do Nothing
                }
                try{
                    if (searchSubfolders){
                        string[] foldersInCurrent = Directory.GetDirectories(currentFolder, "*.*", System.IO.SearchOption.TopDirectoryOnly);
                        foreach (string _current in foldersInCurrent){
                            folderCount.AddRange(foldersInCurrent);
                            folders.Enqueue(_current);
                        }
                    }
                }
                catch{
                    Console.WriteLine("Error: " + currentFolder);
                    // Do Nothing
                }
            }
            countFiles = files.Count();

            List<string> distinct = folderCount.Distinct().ToList(); //Remove Duplicates from scan
            Console.WriteLine("Number of folders AFTER: " + distinct.Count);
            Console.WriteLine("Number of files is: " + files.Count());
       
            Console.ReadLine();
            return files;
        }
    }
}

非常感谢
 
Last edited:

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,605
地点
弗吉尼亚州切萨皮克
编程经验
10+
Directory.GetDirectories() and Directory.GetFiles() returns files and folders with spaces in their name. It also finds the "C:\$Recycle.Bin"
C#:
using System;
using System.IO;

public class Test
{
    static void Main()
    {
        foreach(var f in Directory.GetDirectories(@"C:\"))
        {
            Console.WriteLine(f);
        }
    }
}
Capture.png
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,981
地点
英国
编程经验
10+
您似乎不了解的一件事是,回收站实际上根本不是文件夹。它之所以赢得它的名字是有原因的 箱子。微软非常适合使用双关语。您会看到,回收站实际上是驱动器上的虚拟位置,存储在其中的文件也实际上不在该位置。回收站实际上是从硬盘驱动器分配空间来存储您在虚拟二进制环境中删除的文件。因此,当您删除文件时,它从未真正从您的PC中删除过。取而代之的是,它分配了一种构建块模式,用于容纳文件的原始地址以及所有颗粒,这使它成为我们在Windows中预览时看到的有效文件。

但是,任何带有标记的文件"已删除",只为它分配了一个标志和一个GUID,它为我们设置了文件在操作系统中不可见的功能,但实际上对我们而言在其虚拟目录中是可见的,直到清除垃圾箱为止。清除垃圾箱的过程只是硬盘重新分配这些垃圾箱。 用来构造文件的文件,它们会在光盘分配区域的各个区域分解和分散,在这些区域中将重复使用该文件并将其永久覆盖。这就是为什么即使删除某些内容仍可以从硬盘恢复某些数据的原因。数据仅散布,直到其被调用以在以后的时间重用为止。反正...

这是我在回收站中遍历文件时的样子。这是将它们移至虚拟文件夹的时间。我相信GUID与硬盘驱动器上的下一个地址有关,但是我可以纠正该地址。 :
C:\ $ Recycle.Bin \ S-1-5-21-3775775533-2454628510-745607798-1001 \ $ RYWMCQR.xml
C:\ $ Recycle.Bin\S-1-5-21-3775181533-2454628510-745607798-1001\$RZWSJHT.txt
如果您在回收站的位置上查找目录信息,则一旦进入调试器,您将找到有关它的更多信息。

截图_48.jpg

请注意,它是隐藏的 目录,并且需要提升的权限,因为它的 系统 虚拟的"folder". 最后,无论您打算如何处理回收站中的文件,都可能很难做到。另外,在MSDN上查找KNOWNFOLDERID,因为它可以阐明我上面所说的内容。

编辑
修正了一个错字
 
Last edited:

菲霍维

成员
已加入
2019年11月26日
留言内容
7
编程经验
Beginner
Directory.GetDirectories() and Directory.GetFiles() returns files and folders with spaces in their name. It also finds the "C:\$Recycle.Bin"
C#:
using System;
using System.IO;

public class Test
{
    static void Main()
    {
        foreach(var f in Directory.GetDirectories(@"C:\"))
        {
            Console.WriteLine(f);
        }
    }
}
查看附件722

感谢您的见解,为什么在我的代码中我不能做同样的事情,而我的代码却在下面
这是来自catch块,所有这些文件夹都在E:\下,而不是"E:\ \"-在我的代码上进行了测试。
wtf.png

然后与更多文件夹相同。
wtf2.png

例如...如您所料,它位于文件夹中"E:\[Alt+0160]\", but not "E:\ [Alt + 0160] \ [Alt + 0160] \"(对于空格的不当行为,我们深表歉意)

当我在您的代码中重新创建它时,这仍然是一个问题,
E:\ test \ \ tt(No-BreakSpace是文件夹本身)
我得到输出:"E:\test\ \ \tt"
您似乎不了解的一件事是,回收站实际上根本不是文件夹。它之所以赢得它的名字是有原因的 箱子。微软非常适合使用双关语。您会看到,回收站实际上是驱动器上的虚拟位置,存储在其中的文件也实际上不在该位置。回收站实际上是从硬盘驱动器分配空间来存储您在虚拟二进制环境中删除的文件。因此,当您删除文件时,它从未真正从您的PC中删除过。取而代之的是,它分配了一种构建块模式,用于容纳文件的原始地址以及所有颗粒,这使它成为我们在Windows中预览时看到的有效文件。

但是,任何带有标记的文件"已删除",只为它分配了一个标志和一个GUID,它为我们设置了文件在操作系统中不可见的功能,但实际上对文件的虚拟目录中的文件可见,直到我们清除垃圾箱为止。清除垃圾箱的过程只是硬盘重新分配这些垃圾箱。 用来构造文件的文件,它们会在光盘分配区域的各个区域分解和分散,在这些区域中将重复使用该文件并将其永久覆盖。这就是为什么即使删除某些内容仍可以从硬盘恢复某些数据的原因。数据仅散布,直到其被调用以在以后的时间重用为止。反正...

这是我在回收站中遍历文件时的样子。这是将它们移至虚拟文件夹的时间。我相信GUID与硬盘驱动器上的下一个地址有关,但是我可以纠正该地址。 :

如果您在回收站的位置上查找目录信息,则一旦进入调试器,您将找到有关它的更多信息。

查看附件723
请注意,它是隐藏的 目录,并且需要提升的权限,因为它的 系统 虚拟的"folder"。最后,无论您打算如何处理回收站中的文件,都可能很难做到。另外,抬头 MSDN上的KNOWNFOLDERID,因为它澄清了我上面所说的一些内容。
也感谢您的见解。我知道回收站是如何工作的,我与Non-Break Space混淆了,我认为我的代码不会带有任何特殊字符。
因此,尽管我找不到任何可疑的原因,但是为什么我的非中断空间是在E:\和驱动器上的文件夹本身之间添加的,所以我的代码根本无法工作。
 

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,605
地点
弗吉尼亚州切萨皮克
编程经验
10+
这是渔获物
Say what? If that's in the catch block, then that means an exception is being thrown. What is the exception? Perhaps the message text in the exception will tell you what is failing.
 

菲霍维

成员
已加入
2019年11月26日
留言内容
7
编程经验
Beginner
Say what? If that's in the catch block, then that means an exception is being thrown. What is the exception? Perhaps the message text in the exception will tell you what is failing.
E:\ test \ \ \ tt | System.IO.DirectoryNotFoundException:找不到路径'E:\ test \ \ \ tt'的一部分。
在System.IO .__ Error.WinIOError(Int32 errorCode,可能是StringFullPath)
在System.IO.FileSystemEnumerableIterator`1.CommonInit()
在System.IO.FileSystemEnumerableIterator`1..ctor(字符串路径,字符串originalUserPath,字符串searchPattern,SearchOption searchOption,SearchResultHandler`1 resultHandler,布尔checkHost)
在System.IO.Directory.GetDirectories(字符串路径,字符串searchPattern,SearchOption searchOption)
在D:\ Projects \ FileScan \ FileHandler \ Program.cs:line 34中的FileHandler.Program.GetAllFilesFromFolder(String root,Boolean searchSubfolders)中
 

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,605
地点
弗吉尼亚州切萨皮克
编程经验
10+
看起来.NET Framework在处理不间断空间时有一个错误……据说它已为.NET Core修复,但我猜您正在使用.NET4.x。

请参阅以下评论: Get-ChildItem和不间断空格
 

菲霍维

成员
已加入
2019年11月26日
留言内容
7
编程经验
Beginner
看起来.NET Framework在处理不间断空间时有一个错误……据说它已为.NET Core修复,但我猜您正在使用.NET4.x。

请参阅以下评论: Get-ChildItem和不间断空格
您是对的..我尝试使用.NET Framework 4.7.2,但这样做不行。
输出:E:\ test \ \ \ tt
当我将其放入.NET Core时
得到以下输出:
E:\ test \ \ tt->我需要的是那个。

非常感谢!
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,981
地点
英国
编程经验
10+
顺便说一句,这一定是您的代码中有问题的地方。我使用自己编写的代码在4.7.2中进行了尝试,没有遇到任何问题。在4.8上进行了进一步测试,也没有遇到任何问题。
 

菲霍维

成员
已加入
2019年11月26日
留言内容
7
编程经验
Beginner
我无法确定问题所在...我的代码全都在那里,或多或少..如果您可以在此处发布您的代码以进行比较..那就太好了。当我将它复制到.NET Core时,无需进行编辑,它就可以正常工作。


好吧,我在项目上对.NET Framework所做的编辑只是ClickOnce安全性(左右),它可以绕过问题并使用管理帐户扫描文件夹和文件。但这对我来说毫无意义。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,981
地点
英国
编程经验
10+
绕过问题并使用管理帐户扫描文件夹和文件。
正如我上面已经说过的那样,需要具有管理权限,这可能就是为什么它不起作用的原因。垃圾箱归系统所有。

自从更改以来,您可以先尝试检查它是否在4.7.2中运行吗?

对您来说,唯一的不同是,我使用的是system32的Shell API和接口。
 

菲霍维

成员
已加入
2019年11月26日
留言内容
7
编程经验
Beginner
E:\\\ test | System.IO.DirectoryNotFoundException:找不到路径“ E:\\\ test”的一部分。
在System.IO .__ Error.WinIOError(Int32 errorCode,可能是StringFullPath)
在System.IO.FileSystemEnumerableIterator`1.CommonInit()
在System.IO.FileSystemEnumerableIterator`1..ctor(字符串路径,字符串originalUserPath,字符串searchPattern,SearchOption searchOption,SearchResultHandler`1 resultHandler,布尔checkHost)
在System.IO.Directory.GetDirectories(字符串路径,字符串searchPattern,SearchOption searchOption)
在frameworktest.Program.GetAllFilesFromFolder(字符串根,布尔searchSubfolders)在C:\ Users \ krogi \ source \ repos \ frameworktest \ frameworktest \ Program.cs:line 55
而不是E:\ \ test(\ \ => \[Alt+0160]\)

错误行
Program.cs:
                        string[] foldersInCurrent = Directory.GetDirectories(currentFolder, "*.*", SearchOption.TopDirectoryOnly);

代码块在主(第一)帖子中可用。

因此它仍然无法正常工作。一个普通的新项目,将工作代码从.NET Core复制到.NET Framework 4.7.2,并注释掉了Npgsql(用于C#的PostgreSQL驱动程序),因为我不需要扫描或使用数据库。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,981
地点
英国
编程经验
10+
好吧,这很奇怪。今晚晚些时候我需要运行您的代码并自己调试它。我现在很乐意这样做,但是目前我面临着完成我原本打算在上周完成工作但又完全忘记的事情的压力。无论如何,一旦找到脚步并深入研究,我将为您更新。乍一看,它确实看起来像您对路径的遍历有问题,但是稍后我会为您检查。但是,谢谢您的尝试。 ;)
 

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,605
地点
弗吉尼亚州切萨皮克
编程经验
10+
不只是代码。需要目录的环境中唯一的字符是不间断空格,以复制OP所看到的问题。
 

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,605
地点
弗吉尼亚州切萨皮克
编程经验
10+
我重现了.NET Framework 4.8的问题:
C#:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

class Program
{
    const string RootTestDir = "C:\\TestingNBSPDir";
    static string ChildTestDir = Path.Combine(RootTestDir, "\x00A0\\Leaf");

    static bool EnsureDirectoryExists(string path)
    {
        try
        {
            Console.WriteLine($"Ensuring directory exists: {path}");
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);
            return true;
        }

        catch (Exception ex)
        {
            Console.Error.WriteLine($"Couldn't access or create {ChildTestDir}");
            Console.Error.WriteLine(ex);
        }
        return false;
    }

    static void RecursivelyListDirectories(string root)
    {
        Console.WriteLine($"Enumerating directories starting at: {root}");
        var queue = new Queue<string>();
        queue.Enqueue(RootTestDir);
        while (queue.Count != 0)
        {
            var current = queue.Dequeue();
            Console.WriteLine($"Working on '{current}'");

            try
            {
                foreach (var dir in Directory.EnumerateDirectories(current, "*", SearchOption.TopDirectoryOnly))
                    queue.Enqueue(dir);
            }

            catch (Exception ex)
            {
                Console.Error.WriteLine(ex);
            }
        }
    }

    static void Main()
    {
        if (EnsureDirectoryExists(ChildTestDir))
            RecursivelyListDirectories(RootTestDir);
    }
}

结果是:
Capture.PNG.png


.NET Core 3.0可以正常工作:
Capture2.PNG.png
 
Last edited:

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,605
地点
弗吉尼亚州切萨皮克
编程经验
10+
Even though I used EnumerateDirectories() above, GetDirectories() also fails/works the same way.
 

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,605
地点
弗吉尼亚州切萨皮克
编程经验
10+
另一个数据点是:让.NET Framework 4.8进行递归本身可以正常工作,但是当您实际希望自己遍历目录时,这可以提供一定的舒适性。
C#:
static void RecursivelyListDirectories(string root)
{
    Console.WriteLine($"Enumerating directories starting at: {root}");
    try
    {
        foreach (var dir in Directory.GetDirectories(root, "*", SearchOption.AllDirectories))
            Console.WriteLine($"Working on '{dir}'");
    }

    catch (Exception ex)
    {
        Console.Error.WriteLine(ex);
    }
}
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,981
地点
英国
编程经验
10+
确实没有太多选择,如果我正确阅读了您的主题,那么您提到Clickonce。您将无法通过ClickOnce使用提升的权限,因此请禁用ClickOnce。右键单击您的项目属性,然后单击 安全 并禁用 单击一次安全设置。然后添加一个app.manafest文件并进行编辑 作为调用者 is replaced with : <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />. After you decide how to handle this issue with the white space, you may get access denied errors for some of the root files/folders in C:\ or any other drives you search, but we can deal with that next in a separate topic if you need to.

我唯一能看到的选择是;使用0160防止路径(最佳选择)!您可以防止这样命名文件夹吗?

很有可能不会,除非您的应用程序负责创建文件或文件夹,否则我不建议重命名任何文件或文件夹。 (假设您的应用程序是某种备份程序。)您还可以记录包含0160的路径,并在应用程序中更正它们而不接触原始文件。而且,如果您的应用程序打算备份每个文件,则只需替换0160并通过调用trim()来修复路径,然后以某种方式将该路径标记为需要重命名为0160(如果您需要将其还原为原始路径)姓名。

您可以使用字典或外部文件来跟踪文件/文件夹路径,并以0160的间距和修剪后的版本(非0160)查看它们。假设您的应用程序创建了备份或记录了所有文件/文件夹路径。并且,如果您曾经需要还原文件/目录,则只需检查您的文件以查看是否需要使用0160正式命名需要替换的文件/文件夹,以便可以在OS上复制正确的还原路径。那有意义吗?

重命名的另一种选择是跳过这些文件和文件夹。您可以使用Linq修改您的foreach语句,以检查文件或文件夹是否包含0160。下面将执行的操作是检查该路径是否不包含带有0160的路径,如果包含0160,则跳过该路径:
C#:
foreach (string _current in foldersInCurrent.Where(s => !s.Contains(@" ")))
Simply use .Where(s => !s.Contains(@" ")) wherever you want to exclude or handle paths containing 0160.
显然,我确定您知道您也可以在路径上调用Trim()来删除任何此类空格等。您还应该使用DirInfo / FileInfo构造foreach,以尝试避免此类问题。使用 :
foreach(DirectoryInfo _current等...这将意味着重写一些代码。
使用简单的字符串不能很好地使用面向对象的代码。您可以改用Dirinfo / FileInfo来利用更好的功能,并从那里开始您的路径。希望这些建议对您有所帮助。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,981
地点
英国
编程经验
10+
补充一点,今天是凌晨4点,经过漫长的一天后我有点劳累,所以我希望这是有道理的,如果有不清楚的地方,我明天会与您联系并提出任何问题你可能有。 (如果我可以醒来。)

晚安。
 

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,605
地点
弗吉尼亚州切萨皮克
编程经验
10+
快速提醒一下,不间断空格(160)仅当它是.NET Framework 4.6.1及更高版本的尾随字符时才是问题。如果它(和其他空白字符)不是结尾字符,则没有问题。绕过该问题的另一种方法是转到.NET Core 3.0。
 
Last edited:
最佳 底部