有什么方法可以从文件路径中获取 Environment.SpecialFolder 吗?

Is there any way to get Environment.SpecialFolder from file path?

我有一个要求,我想从文件路径中获取 Environment.SpecialFolder 值。

例如-

string filePath = @"C:\Program Files (x86)\text.txt"
//SpecialFolder sf = GetSpecialFolderAssociatedWithPath(filePath); // sf will be ProgramFilesX86
//Need something similar

我想进一步使用 sf 生成另一条路径,所以如果我们得到对应于特定 SpecialFolder 的路径,那也可以。

你可以这样做(假设你想获得特殊文件夹的实际枚举值):

public static Environment.SpecialFolder? FindSpecialFolder(string filePath)
{
    filePath = Path.GetFullPath(filePath);

    foreach (var folder in Enum.GetValues<Environment.SpecialFolder>())
    {
        string directory = Environment.GetFolderPath(folder);

        if (directory.Length > 0 && filePath.StartsWith(directory, StringComparison.OrdinalIgnoreCase))
            return folder;
    }

    return null;
}

请注意,我必须 return 一个可空值,因为 Microsoft 未能遵循他们自己的准则并且没有在 Environment.SpecialFolder 枚举中包含特殊的“None”零值我可以 return 表示“未找到”。

用法是这样的:

string filePath = @"C:\Program Files (x86)\text.txt";

var folder = FindSpecialFolder(filePath);

if (folder == null)
    Console.WriteLine("No folder found");
else
    Console.WriteLine(folder.Value);

如果你想要路径和枚举值,你可以 return 它们都在一个元组中:

public static (Environment.SpecialFolder? specialFolder, string? directory) FindSpecialFolder(string filePath)
{
    filePath = Path.GetFullPath(filePath);

    foreach (var folder in Enum.GetValues<Environment.SpecialFolder>())
    {
        string directory = Environment.GetFolderPath(folder);

        if (directory.Length > 0 && filePath.StartsWith(directory, StringComparison.OrdinalIgnoreCase))
            return (folder, directory);
    }

    return default;
}

你可以像这样使用:

var folder = FindSpecialFolder(filePath);

if (folder.specialFolder == null)
    Console.WriteLine("No folder found");
else
    Console.WriteLine($"{folder.specialFolder.Value}: {folder.directory}");

实际上,某些特殊文件夹可能嵌套在其他特殊文件夹下,例如您可能有:

C:\Path1\Path2
C:\Path1\Path2\Path3

在那种情况下,上面的代码将 return 它匹配的第一条路径,而不是最长的;即寻找 "C:\Path1\Path2\Path3\SomeFile.txt" 可能 return "C:\Path1\Path2" 的特殊文件夹而不是 "C:\Path1\Path2\Path3".

的特殊文件夹

如果你想处理这种可能性,你必须找到最长的匹配路径,例如:

public static (Environment.SpecialFolder? specialFolder, string? directory) FindSpecialFolder(string filePath)
{
    filePath = Path.GetFullPath(filePath);

    int longest = 0;
    Environment.SpecialFolder? longestFolder = null;
    string? longestDir = null;

    foreach (var folder in Enum.GetValues<Environment.SpecialFolder>())
    {
        string directory = Environment.GetFolderPath(folder);

        if (directory.Length > longest && filePath.StartsWith(directory, StringComparison.OrdinalIgnoreCase))
        {
            longestDir    = directory;
            longestFolder = folder;
            longest       = directory.Length;
        }
    }

    return (longestFolder, longestDir);
}

并像这样使用它:

var folder = FindSpecialFolder(filePath);

if (folder.specialFolder == null)
    Console.WriteLine("No folder found");
else
    Console.WriteLine($"{folder.specialFolder.Value}: {folder.directory}");

另一件需要注意的事情是多个特殊文件夹可能具有相同的路径。在这种情况下,无法区分它们,因此代码将 return 它找到的第一个匹配项。

还要注意filePath = Path.GetFullPath(filePath);的使用,以确保将相对路径转换为绝对路径,否则匹配可能无法进行。

我不知道有任何现有的功能可以做到这一点,但是自己动手并不难。例如:

private static bool TryGetSpecialFolderAssociatedWithPath(string filePath, out Environment.SpecialFolder sf)
{
    foreach (Environment.SpecialFolder value in Enum.GetValues(typeof(Environment.SpecialFolder)))
    {
        string path = Environment.GetFolderPath(value);
        if (!string.IsNullOrEmpty(path) && filePath.StartsWith(path + "\"))
        {
            sf = value;
            return true;
        }
    }

    sf = default; // Actually the same as Desktop
    return false;
}

用法:

string filePath = @"C:\Program Files (x86)\text.txt";
if (TryGetSpecialFolderAssociatedWithPath(filePath, out Environment.SpecialFolder sf))
{
    Console.WriteLine("Special folder is " + sf);
}

这会产生以下输出:

Special folder is ProgramFilesX86

需要注意的一点是我在返回的路径上附加了一个反斜杠。如果我不这样做,代码片段会在 ProgramFilesX86.

之前到达 ProgramFiles