C# Filepath Recasing

≡放荡痞女 提交于 2019-12-18 11:45:18

问题


I'm trying to write a static member function in C# or find one in the .NET Framework that will re-case a file path to what the filesystem specifies.

Example:

string filepath = @"C:\temp.txt";
filepath = FileUtility.RecaseFilepath(filepath);

// filepath = C:\Temp.TXT
// Where the real fully qualified filepath in the NTFS volume is C:\Temp.TXT

I've tried the following code below and many variants of it and it still doesn't work. I know Windows is case-insensitive in general but I need to pass these file paths to ClearCase which considers file path casing since it's a Unix and Windows application.

public static string GetProperFilePathCapitalization(string filepath)
{
    string result = "";

    try
    {
        result = Path.GetFullPath(filepath);
        DirectoryInfo dir = new DirectoryInfo(Path.GetDirectoryName(result));
        FileInfo[] fi = dir.GetFiles(Path.GetFileName(result));
        if (fi.Length > 0)
        {
            result = fi[0].FullName;
        }
    }
    catch (Exception)
    {
        result = filepath;
    }

    return result;
}

回答1:


This is a pretty simple implementation that assumes that the file and directories all exist and are accessible:

static string GetProperDirectoryCapitalization(DirectoryInfo dirInfo)
{
    DirectoryInfo parentDirInfo = dirInfo.Parent;
    if (null == parentDirInfo)
        return dirInfo.Name;
    return Path.Combine(GetProperDirectoryCapitalization(parentDirInfo),
                        parentDirInfo.GetDirectories(dirInfo.Name)[0].Name);
}

static string GetProperFilePathCapitalization(string filename)
{
    FileInfo fileInfo = new FileInfo(filename);
    DirectoryInfo dirInfo = fileInfo.Directory;
    return Path.Combine(GetProperDirectoryCapitalization(dirInfo),
                        dirInfo.GetFiles(fileInfo.Name)[0].Name);
}

There is a bug with this, though: Relative paths are converted to absolute paths. Your original code above did the same, so I'm assuming that you do want this behavior.




回答2:


The below works fine to the extent I tested... only catch is that the API used is available only in Vista.

static void Main(string[] args)
{
    using (FileStream fs = File.OpenRead(@"D:\temp\case\mytest.txt"))
    {
        StringBuilder path = new StringBuilder(512);
        GetFinalPathNameByHandle(fs.SafeFileHandle.DangerousGetHandle(), path, path.Capacity, 0);
        Console.WriteLine(path.ToString());
    }
}

[DllImport("kernel32.dll", SetLastError = true)]
static extern int GetFinalPathNameByHandle(IntPtr handle, [In, Out] StringBuilder path, int bufLen, int flags);



回答3:


You can search for the file you want to get the case on and return the results of your search (you want to check the casing of a file that exists, right?). Something like this:

public static string GetProperFilePathCapitalization(string filepath) {
   string directoryPath = Path.GetDirectoryName(filepath);
   string[] files = Directory.GetFiles(directoryPath, Path.GetFileName(filepath));
   return files[0];
}

Is this what you're looking for?




回答4:


I have something more efficient but:

1) It doesn't seem to work for all cases. (I've not figured out the pattern of which files and directories it correctly gets the casing, and which ones it does not.)

2) It's Windows specific.

static string GetProperFilePathCapitalization1(string filename)
{
    StringBuilder sb = new StringBuilder(260);
    int length = GetLongPathName(filename, sb, sb.Capacity);

    if (length > sb.Capacity)
    {
        sb.Capacity = length;
        length = GetLongPathName(filename, sb, sb.Capacity);
    }

    if (0 == length)
        throw new Win32Exception("GetLongPathName");

    return sb.ToString();
}

[DllImport("kernel32.dll")]
static extern int GetLongPathName(string path, StringBuilder pszPath, int cchPath);



回答5:


The answer by @Ants above should absolutely get credit as the accepted answer. However, I refactored it a bit to my purposes. The approach is packaged as extension methods for FileInfo and DirectoryInfo, and return corrected ones as well.

public static DirectoryInfo GetProperCasedDirectoryInfo(this DirectoryInfo dirInfo)
{
    // Inspired by http://stackoverflow.com/a/479198/244342

    if (!dirInfo.Exists)
    {
        // Will not be able to match filesystem
        return dirInfo;
    }

    DirectoryInfo parentDirInfo = dirInfo.Parent;
    if (parentDirInfo == null)
    {
        return dirInfo;
    }
    else
    {
        return parentDirInfo.GetProperCasedDirectoryInfo().GetDirectories(dirInfo.Name)[0];
    }
}

public static FileInfo GetProperCasedFileInfo(this FileInfo fileInfo)
{
    // Inspired by http://stackoverflow.com/a/479198/244342

    if (!fileInfo.Exists)
    {
        // Will not be able to match filesystem
        return fileInfo;
    }

    return fileInfo.Directory.GetProperCasedDirectoryInfo().GetFiles(fileInfo.Name)[0];
}

I've been banging my head over some case-inconsistency issues with FileInfo. In order to ensure robustness, I convert to all caps when doing comparison or storage of the paths. To clarify the intent of the code, I also have these extension methods:

public static string GetPathForKey(this FileInfo File)
{
    return File.FullName.ToUpperInvariant();
}

public static string GetDirectoryForKey(this FileInfo File)
{
    return File.DirectoryName.ToUpperInvariant();
}



回答6:


You'll want the system to find the file for you. I do this by pretending that I do not know the exact path, i.e. have the system search:

var fileName = Path.GetFileName(filePath);
var dir = Path.GetDirectoryName(filePath);
var filePaths = Directory.GetFiles(dir, fileName, SearchOption.TopDirectoryOnly);
var caseCorrectedFilePath = filePaths.FirstOrDefault();

So we search in the directory, filtering on the exact file name and limiting the search to the current directory only (no recursion).

This returns a string array containing either the single file path with correct casing (if the file exists) or nothing (if the file does not exist).

One warning: You may need to disallow wildcards in the input path, because this approach accepts them and may find multiple files as a result.

Edit

The drive letter appears to still follow the casing that we provide. Also, this needs to be tested for UNC paths.



来源:https://stackoverflow.com/questions/478826/c-sharp-filepath-recasing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!